在.NET Core MVC中如何通过Simple Injector从事件处理器访问变量
问题描述
我正在开发一个.NET Core MVC Web应用。我有一个领域对象方法,用于执行业务逻辑并返回一个包含传递给视图模型变量的结果对象。若目标事件触发,该结果对象中的部分变量来自事件处理器。我想了解如何通过Simple Injector这类依赖注入(DI)容器,以更优雅的方式从事件处理器中访问这些变量。以下是我当前实现的简化代码片段:
类DomainA(包含领域逻辑)
private DomainB _domainBObj; /************** * constructor ***************/ public DomainA(){ _domainBObj = new DomainB(); } /******************************** * called in Application Service ********************************/ public MyEventResult doSth(string arg){ /************************************************************************ * 这段代码能工作,但我在想有没有更优雅的方式,让DomainA在事件触发时能获取事件结果, * 而不需要在这里通过DI容器直接获取事件处理器 *************************************************************************/ var eventHandler = DIContainer.INSTANCE.getInstance<MyEventHandler>(); _domainBObj.doSth(arg); return doSthBasedOnEventResult(eventHandler.Result); } public MyEventResult doSthBasedOnEventResult(EventResult eventResult){ var ret = new MyEventResult(); // 设置MyEventResult的变量 return ret; }
类DomainB(第三方库对象的包装类)
/************** * constructor ***************/ public DomainB(){ // 第三方库对象配置 some3rdPartyLibraryObj.OnSomeEvent( var @event = new MyEvent(); // 包装了一些变量的事件对象 var eventBus = DIContainer.INSTANCE.getInstance<MyEventBus>(); eventBus.Dispatch(@event); ) } /*************************** * 在DomainA的doSth()中被调用 ***************************/ public void doSth(string arg){ some3rdPartyLibraryObj.doSth(arg); }
事件总线(单例作用域)
public interface IEventBus { void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent; } public class MyEventBus : IEventBus { public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent { if (@event == null) throw new ArgumentNullException("event"); var handlerType = typeof(IEventHandler<>).MakeGenericType(@event.GetType()); dynamic handlers = DIContainer.INSTANCE.GetAllInstances<IEventHandler<TEvent>>(); foreach (var handler in handlers) { handler.Handle(@event); } }
事件处理器(请求作用域)
public interface IEventHandler<TEvent> { void Handle(TEvent @event); } public class MyEventHandler : IEventHandler<MyEvent> { public EventResult Result { get; private set; } public void Handle(MyEvent @event) { var result = new EventResult(); // 设置EventResult的变量 Result = result; } }
解决方案
你的核心问题在于领域层直接依赖DI容器,这违反了依赖倒置原则,同时导致代码耦合度高、难以测试。下面是基于Simple Injector的优化方案,核心思路是通过依赖注入传递所需依赖,而非直接从容器获取,同时调整事件结果的传递方式。
1. 移除领域类中的容器依赖
首先,DomainA和DomainB不应该直接引用DIContainer,应该通过构造函数注入所需的依赖:
修改DomainA
private readonly DomainB _domainBObj; private readonly IEventHandler<MyEvent> _myEventHandler; // 通过构造函数注入依赖 public DomainA(DomainB domainBObj, IEventHandler<MyEvent> myEventHandler) { _domainBObj = domainBObj ?? throw new ArgumentNullException(nameof(domainBObj)); _myEventHandler = myEventHandler ?? throw new ArgumentNullException(nameof(myEventHandler)); } public MyEventResult doSth(string arg) { _domainBObj.doSth(arg); return doSthBasedOnEventResult(_myEventHandler.Result); } // 其余代码不变
修改DomainB
private readonly IEventBus _eventBus; // 注入EventBus,而非直接从容器获取 public DomainB(IEventBus eventBus) { _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); some3rdPartyLibraryObj.OnSomeEvent(() => { var @event = new MyEvent(); _eventBus.Dispatch(@event); }); } // 其余代码不变
2. 优化事件总线的实现
你的MyEventBus当前直接依赖容器实例,应该改为通过构造函数注入容器,利用Simple Injector的内置功能解析处理器:
public class MyEventBus : IEventBus { private readonly Container _container; public MyEventBus(Container container) { _container = container ?? throw new ArgumentNullException(nameof(container)); } public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent { if (@event == null) throw new ArgumentNullException(nameof(@event)); // 使用Simple Injector解析该事件的所有处理器 var handlers = _container.GetAllInstances<IEventHandler<TEvent>>(); foreach (var handler in handlers) { handler.Handle(@event); } } }
3. 配置Simple Injector容器
在应用启动时(如Startup.cs),正确注册所有依赖并指定生命周期:
var container = new Container(); // 注册单例服务 container.Register<IEventBus, MyEventBus>(Lifestyle.Singleton); // 注册请求作用域的事件处理器 container.Register<IEventHandler<MyEvent>, MyEventHandler>(Lifestyle.Scoped); // 注册领域类 container.Register<DomainB>(Lifestyle.Scoped); container.Register<DomainA>(Lifestyle.Scoped); // 验证容器配置,确保所有依赖都正确注册 container.Verify();
4. 可选:更优雅的事件结果传递方式
如果EventResult是事件处理的直接产物,你可以考虑将Result直接附加到MyEvent对象中,而非存储在处理器里,进一步解耦:
修改MyEvent类
public class MyEvent : IEvent { public EventResult Result { get; set; } // 原有的其他属性 }
修改事件处理器
public class MyEventHandler : IEventHandler<MyEvent> { public void Handle(MyEvent @event) { @event.Result = new EventResult(); // 设置EventResult的变量 } }
修改DomainB的事件触发逻辑
some3rdPartyLibraryObj.OnSomeEvent(() => { var @event = new MyEvent(); _eventBus.Dispatch(@event); // 如果需要,这里也可以直接使用@event.Result });
修改DomainA的逻辑(直接获取事件结果)
如果DomainA需要直接获取事件结果,可让DomainB的doSth方法返回结果:
// DomainB的doSth方法修改 public EventResult doSth(string arg) { EventResult result = null; // 临时订阅事件获取结果(需第三方库支持移除事件) void TempHandler() { var @event = new MyEvent(); _eventBus.Dispatch(@event); result = @event.Result; } some3rdPartyLibraryObj.OnSomeEvent(TempHandler); some3rdPartyLibraryObj.doSth(arg); some3rdPartyLibraryObj.RemoveSomeEvent(TempHandler); return result; } // DomainA中调用 public MyEventResult doSth(string arg) { var eventResult = _domainBObj.doSth(arg); return doSthBasedOnEventResult(eventResult); }
关键改进点总结
- 移除所有领域类对DI容器的直接依赖,通过构造函数注入实现解耦
- 遵循依赖倒置原则,依赖抽象而非具体实现
- 利用Simple Injector的生命周期管理(如请求作用域)确保处理器实例的正确性
- 可选:将事件结果与事件对象绑定,避免依赖处理器的状态
内容的提问来源于stack exchange,提问作者anndexi99
相关产品推荐
相关产品推荐

