【问题标题】:Blazor: Event sent from Service to Component is nullBlazor:从服务发送到组件的事件为空
【发布时间】:2020-05-08 11:47:03
【问题描述】:

我有一个由 Blazor 组件和 Worker Service 组成的程序。我正在使用 .NET CORE 3.1

我的代码基于手动到Blazor components

在我的工作人员服务方面,我有:

    public event Func<double, Task> Notify;
    public double Data{ get; set; }

    public async Task Update(double data)
    {
        if (Notify != null)
        {
            await Notify.Invoke(data).ConfigureAwait(false);
        }
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
             await Update(Data).ConfigureAwait(false);
             ...

             await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
        }
    }

在我的 Blazor 组件中:

@using MyNamespace
@inject WorkerService MyService
@implements IDisposable

<div>@DataToDisplay</div>

@code {

    private double DataToDisplay{ get; set; }

    protected override void OnInitialized()
    {
        MyService.Notify += OnNotify;
    }

    public async Task OnNotify(double data)
    {
        await InvokeAsync(() =>
        {
            DataToDisplay= data;
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        MyService.Notify -= OnNotify;
    }
}

调试后我通知,Notify 事件在 Blazor 组件中正确连接,在 OnInitialized() 方法中(我也尝试过带有 OnInitializedAsync() 的版本)但是,每次,当服务调用 Update() 方法时,在条件检查:if (Notify != null),Notify 为空。

我找不到原因。感谢您的任何建议!

【问题讨论】:

  • 你的依赖注入看起来像什么? MyService 是单例吗?
  • 确实如此。 Startup.cs 包含即:services.AddSingleton&lt;WorkerService&gt;();
  • 谁在启动服务?我想通过调用 ExecuteAsync,对吧?在不使用 ConfigureAwait 的情况下尝试您的代码...也许问题出在此处。你永远不会知道...
  • 不是ConfigureAwait。我以两种方式尝试了上面的代码。拳头我没有将服务实现为托管服务。我添加了一个按钮来启动 ExecuteAsync 方法。 UI 得到了预期的更新。我把服务实现改成了托管服务,所以需要把DI注册改成AddHostedService。这导致了一个错误,表明该服务未添加到 DI 并且组件无法注入它。也添加了一个单音。没有错误,但是 UI 没有更新,并且 Notify 为空。实际上,这样你就有了 2 个独立的服务实例。
  • 当然上面的评论假设这发生了。我能够通过创建一个不同的非托管服务来完成这项工作,该服务注入到它自己的组件和托管服务中。托管服务调用注入服务的更新方法。事件的实现从托管服务转移到可注入服务。

标签: events .net-core service-worker blazor


【解决方案1】:
public class InjectableService
    {
        public double Value { get; set; }
        public event Func<Task> Notify;
        public double Data { get; set; }

        public  async Task RefreshAsync(double value)
        {
            if (Notify is { })
            {
                Value = value;
                await Notify.Invoke();
            }
        }
public class MyService : BackgroundService
    {
        private readonly InjectableService _injectableService;

        public MyService(InjectableService injectableService)
        {
            _injectableService = injectableService;
        }

        public async Task Update(double value)
        {
            await _injectableService.RefreshAsync(value);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await Update(_injectableService.Value + 0.1).ConfigureAwait(false);
                await Task.Delay(1000).ConfigureAwait(false);
            }
        }
    }
@inject InjectableService MyService
@implements IDisposable

<p>@MyService.Value</p>

@code {
    protected override void OnInitialized()
    {
        MyService.Notify += OnNotify;
    }

    public async Task OnNotify()
    {
        await InvokeAsync(() =>
        {
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        MyService.Notify -= OnNotify;
    }
}

在 Startup.cs 中:

services.AddHostedService<MyService>();
services.AddSingleton<InjectableService>();

【讨论】:

  • 谢谢。您的观察是有道理的,并解释了为什么 HostedService 中的事件为空 - 必须运行该服务的两个实例。我会尝试您的解决方案作为解决方法。这是一种奇怪的行为,服务的创建次数是单例的两倍。
  • 我在这里找到了描述:github.com/dotnet/extensions/issues/553 不应将托管服务视为 API(第二条评论)。注入中间服务真的很有帮助。尽管如此,我还是不明白,为什么托管服务甚至设置为 Singleton 的行为都不是那样的。谢谢@ZsoltBendes!
【解决方案2】:

基于github上的讨论:https://github.com/dotnet/extensions/issues/553 从 .NET Core 2.1 开始,我认为 HostedServices 是瞬态的。

但是有可能将其用作 API,该线程底部所描述的内容:

在 Startup.cs 中,在 ConfigureServices(IServiceCollection services):

services.AddSingleton<BackgroundWorkerService>();
services.AddSingleton<IHostedService>(p => p.GetService<BackgroundWorkerService>());

这显然是一种魅力。

【讨论】:

    【解决方案3】:

    我遇到了和你类似的问题,因为我想通过使用事件来更新 UI 中的一些元素。

    查看服务注册方法,来自微软官方文档hereservices.AddSingleton&lt;IMyDep, MyDep&gt;(); 是要走的路,因为它允许多个实现。

    我所要做的就是创建一个带有接口的类:

    public interface INotifierService
    {
        // In an Interface, its all Public!
    
    
        delegate void UiChangedEventHandler(object source, EventArgs args);
    
        event UiChangedEventHandler UiChanged; //Handler
    
    
    }
    

    以及从该接口派生的另一个类:

    public class NotifierService:INotifierService
    {
    
        public event INotifierService.UiChangedEventHandler UiChanged; //Event from the base Interface
    
        protected virtual void OnUiChanged()
        {
            UiChanged?.Invoke(this, EventArgs.Empty);
        }
    
    }
    

    并在Startup.cs:注册他们

            services.AddSingleton<Services.INotifierService, Services.NotifierService>();
    

    效果很好!

    当然,不要忘记将接口注入到您的 .razor 文件中并订阅它的事件!

    【讨论】:

      猜你喜欢
      • 2021-09-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-20
      • 2020-04-06
      • 1970-01-01
      • 2021-03-17
      • 2021-01-01
      • 1970-01-01
      相关资源
      最近更新 更多