You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 09:20:19