C# Net核心:DI和事件
我有一个ASP.NET核心项目,其中有一个类C# Net核心:DI和事件,c#,events,asp.net-core,dependency-injection,C#,Events,Asp.net Core,Dependency Injection,我有一个ASP.NET核心项目,其中有一个类DataDistaptcher,它根据事件实现分派数据,另一个类LocationFilter,它正在侦听此事件,以便根据分派的数据执行一些操作 在DataDispatcher中有一种方法: public void UpdateData(string path) { //Upload Data ... //Fire event OnDataUpdated(EventArgs.Emp
DataDistaptcher
,它根据事件实现分派数据,另一个类LocationFilter
,它正在侦听此事件,以便根据分派的数据执行一些操作
在DataDispatcher
中有一种方法:
public void UpdateData(string path)
{
//Upload Data
...
//Fire event
OnDataUpdated(EventArgs.Empty);
}
LocationFilter
构造函数类似于:
public LocationFilter(IDataDispatcher dispatcher)
{
dispatcher.DataUpdated += new EventHandler((o,e) => UpdateData());
}
我在我的项目中使用依赖注入,我想在应用程序开始时更新数据,因此我从IServiceProvider
获取DataDispatcher
,并在app.UseMvc()之后更新
如果我将dispatcher update移动到Controller
,则会触发事件,并且LocationFilter
确实已调度数据
因此,我不想对每个请求都触发更新,我只想在开始时进行更新,所以我应该将dispatcher.update()
方法放在哪里?据我所知,您的singleton LocationFilter对象仅在第一次调用控制器时创建(其构造函数是特定的)。这就是数据丢失的原因
在Configure startup方法中显式创建dispatcher时,尚未创建LocationFilter singleton对象。它仅在“第一次”被请求时创建(即,第一次调用控制器的构造函数-之后将使用相同的单例对象)
“Singleton生存期服务是在第一次请求时(或在运行ConfigureServices并使用服务注册指定实例时)创建的。”
添加singleton服务时,可以在ConfigureServices方法本身中显式创建LocationFilter singleton对象。根据您创建它的方式,您可能还需要处理它
看完整的。。阅读Singleton和Service lifetime部分。我认为这里最好的解决方案是实现IHostedService接口并将您的调用添加到StartAsync方法中。运行asp.net核心应用程序时,该应用程序从DI容器获取所有iHostedService并执行start方法。当应用程序关闭时,将调用StopAsync方法。有关更多信息,请参阅本文档:
代码基于ASP.NET Core 3.1
您可以这样更改LocationFilter:
public class LocationFilter
{
public LocationFilter()
{ } //constructor without dataDispatcher
public void SubscribeToDataDispatcher(DataDispatcher instance)
{
// attach your event
instance.DataUpdated += new EventHandler((o,e) => UpdateData());
}
}
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;
}
}
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)
{ }
}
现在,您的托管服务将如下所示:
public class LocationFilter
{
public LocationFilter()
{ } //constructor without dataDispatcher
public void SubscribeToDataDispatcher(DataDispatcher instance)
{
// attach your event
instance.DataUpdated += new EventHandler((o,e) => UpdateData());
}
}
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;
}
}
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)
{ }
}
最后一步,您必须将托管服务添加到Startup.cs
中的依赖项注入容器中,以便在应用程序中执行该服务
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<UpdateDataService>();
}
public void配置服务(IServiceCollection服务)
{
services.AddHostedService();
}
额外说明:
上面编写的代码只演示了如何在应用程序启动时附加事件。如果只在应用程序启动时触发事件,我不建议使用类似的事件。更好的方法是将DataDispatcher转换为托管服务,并为其提供对LocationFilter实例的引用。大概是这样的:
public class LocationFilter
{
public LocationFilter()
{ } //constructor without dataDispatcher
public void SubscribeToDataDispatcher(DataDispatcher instance)
{
// attach your event
instance.DataUpdated += new EventHandler((o,e) => UpdateData());
}
}
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;
}
}
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)
{ }
}
公共接口IDataDispatcherEventListener
{
void OnEvent(数据模型数据);
}
公共类位置筛选器:IDataDispatcherEventListener
{
public void OnEvent(数据模型数据)
{
//用你的数据做任何事
}
}
公共类数据调度器:IHostedService
{
私有只读IEnumerable\u事件侦听器;
公共数据调度器(IEnumerable eventListeners)
{
_eventListeners=eventListeners;
}
公共任务StartSync(CancellationToken CancellationToken)
{
var workResult=DoWork();
foreach(事件监听器中的var监听器)
{
OnEvent(workResult);
}
返回Task.CompletedTask;
}
公共任务StopAsync(CancellationToken CancellationToken)
{ }
}
现在,您的工作已在启动时完成,您的位置过滤器将收到工作通知。
通过实现此接口,可以删除DataDispatcher和LocationFilter之间的依赖关系。通过使用IEnumerable
,asp.net核心环境将注入您在构造函数中注册的IDataDispatcherEventListener的所有实例,因此,如果您也想通知其他服务,您只需要在DI容器中注册它。为什么要这样解决?为什么不使用将选项注入过滤器?