简体   繁体   English

ASP.Net 核心:DI 和事件

[英]ASP.Net Core: DI and Events

I have a ASP.NET Core project in which I have one class DataDistaptcher that dispatches data based on an event implementation, and another class LocationFilter that is listening to this event to do some stuff based on the dispatched data.我有一个 ASP.NET Core 项目,其中有一个类DataDistaptcher根据事件实现调度数据,另一个类LocationFilter侦听此事件以根据调度的数据执行一些操作。

Inside the DataDispatcher there is a method:DataDispatcher有一个方法:

public void UpdateData(string path)
{   
        //Upload Data
        ...

        //Fire  event
        OnDataUpdated(EventArgs.Empty);

}

The LocationFilter constructor is like: LocationFilter构造函数类似于:

public LocationFilter(IDataDispatcher dispatcher)
{
        dispatcher.DataUpdated += new EventHandler((o,e) => UpdateData());
}

I'm using dependency injection in my project, and I want to update data at the start of the app, so I get DataDispatcher from IServiceProvider and update after app.UseMvc()我用在我的项目依赖注入,我想在应用的开始更新数据,所以我得到DataDispatcherIServiceProvider后更新app.UseMvc()

 // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {   
        services.AddSingleton<ILocationFilter, LocationFilter>();
        services.AddSingleton<IDataDispatcher, DataDispatcher>();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider)
    {   

        app.UseMvc();

        var dispatcher = provider.GetRequiredService<IDataDispatcher>();

        dispatcher.UpdateData("File path");
    }

Now I have a controller where LocationFilter is injected:现在我有一个注入LocationFilter的控制器:

public Controller(ILocationFilter filter)
{
     //Filter dosen't contain data from dispatcher
}

If I move dispatcher update to Controller , the event fires and LocationFilter do have the data dispatched.如果我将调度程序更新移动到Controller ,则事件会触发并且LocationFilter确实会调度数据。

So I don't want to fire updates with every request, I want update only at start, so where should I put the dispatcher.update() method?所以我不想每次请求都触发更新,我只想在开始时更新,那么我应该把dispatcher.update()方法放在哪里?

I think the best solution here is to implement the IHostedService interface and add your call to the StartAsync method.我认为这里最好的解决方案是实现 IHostedService 接口并将您的调用添加到 StartAsync 方法。 When you run an asp.net core app, the app takes all the IhostedServices from the DI container and executes the start method.当您运行 asp.net core 应用程序时,该应用程序从 DI 容器中获取所有 IhostedServices 并执行 start 方法。 When the application shuts down, the StopAsync method will be called.当应用程序关闭时,将调用 StopAsync 方法。 See this documentation for more info: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio有关详细信息,请参阅此文档: https : //docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio

code is based on ASP.NET Core 3.1代码基于 ASP.NET Core 3.1
You might change your LocationFilter like this:您可以像这样更改 LocationFilter:

public class LocationFilter
{
    public LocationFilter()
    { } //constructor without dataDispatcher

    public void SubscribeToDataDispatcher(DataDispatcher instance)
    {
        // attach your event
        instance.DataUpdated += new EventHandler((o,e) => UpdateData());
    }
}

Now your Hosted Service would look like this:现在您的托管服务将如下所示:

public class UpdateDataService : IHostedService
{
    private readonly DataDispatcher _dataDispatcher;
    private readonly LocationFilter _locationFilter;

    public UpdateDataService(DataDispatcher dataDispatcher, LocationFilter locationFilter)
    {
        _dataDispatcher = dataDispatcher;
        _locationFilter = locationFilter;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // connect the locationfilter to the data dispatcher and update the data.
        _locationFilter.SubscribeToDataDispatcher(_dataDispatcher);
        _dataDispatcher.UpdateData();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // detach your event?
        return Task.CompletedTask;
    }
}

As the last step, you have to add your hosted service to the dependency injection container in Startup.cs so that it is executed in your app.作为最后一步,您必须将您的托管服务添加到Startup.cs的依赖项注入容器,以便它在您的应用程序中执行。

public void ConfigureServices(IServiceCollection services)
{
    services.AddHostedService<UpdateDataService>();
}

Extra note:额外说明:

The code written above only demonstrates how to attach events on application startup.上面编写的代码仅演示了如何在应用程序启动时附加事件。 I do not recommend using events like this if you only fire the event on application startup.如果您只在应用程序启动时触发事件,我不建议使用这样的事件。 The better approach would be to turn your DataDispatcher into a hosted service and give it a reference to your LocationFilter instance.更好的方法是将您的 DataDispatcher 转换为托管服务,并为其提供对 LocationFilter 实例的引用。 Something like this:像这样的东西:

public interface IDataDispatcherEventListener
{
    void OnEvent(DataModel data);
}

public class LocationFilter : IDataDispatcherEventListener
{
    public void OnEvent(DataModel data)
    {
        // do whatever with your data
    }
}

public class DataDispatcher : IHostedService
{
    private readonly IEnumerable<IDataDispatcherEventListener> _eventListeners;

    public DataDispatcher(IEnumerable<IDataDispatcherEventListener> eventListeners)
    {
        _eventListeners = eventListeners;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        var workResult = DoWork();
        foreach(var listener in _eventListeners)
        {
            listener.OnEvent(workResult);
        }
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    { }
}

Now your work is done on startup and your location filter will be notified of the work.现在您的工作已在启动时完成,您的位置过滤器将收到工作通知。 By implementing this interface, you remove the dependency between DataDispatcher and LocationFilter.通过实现此接口,您可以删除 DataDispatcher 和 LocationFilter 之间的依赖关系。 By using an IEnumerable<IDataDispatcherEventListener> , the asp.net core environment will inject all instances of IDataDispatcherEventListener that you registered in the constructor, so if you want to notify a different service as well, you only need to register it in the DI container.通过使用IEnumerable<IDataDispatcherEventListener> ,asp.net 核心环境将注入您在构造函数中注册的所有IDataDispatcherEventListener 实例,因此如果您还想通知不同的服务,您只需要在 DI 容器中注册它。

From what I see your singleton LocationFilter object is only created on the first call to your controller (its constructor to be specific).从我所看到的,您的单例 LocationFilter 对象仅在第一次调用您的控制器时创建(具体来说就是它的构造函数)。 Which is why the data is missing.这就是数据丢失的原因。

While you are explicitly creating the dispatcher in the Configure startup method, the LocationFilter singleton object is not yet created.当您在 Configure 启动方法中显式创建调度程序时,尚未创建 LocationFilter 单例对象。 It is only created the "first" time it is requested (ie the first time your Controller's constructor is called - after which the same Singleton object would be used).它仅在“第一次”被请求时创建(即第一次调用 Controller 的构造函数 - 之后将使用相同的 Singleton 对象)。

"Singleton lifetime services are created the first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration)." “单例生命周期服务是在第一次被请求时创建的(或者在运行 ConfigureServices 并通过服务注册指定一个实例时)。”

You can explicitly create your LocationFilter singleton object in your ConfigureServices method itself while adding the Singleton Service.在添加单例服务时,您可以在 ConfigureServices 方法本身中显式创建 LocationFilter 单例对象。 Depending on how you are creating it, you may have to take care of disposing it as well.根据您创建它的方式,您可能还需要处理它。

Look at the complete documentation here .. Read the Singleton and Service lifetime sections.查看此处的完整文档.. 阅读单例和服务生命周期部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM