.net Core 6 Web Api 中的调解器 Objectdisposedexception

Mediator in .net Core 6 WebApi Object disposed exception

我有以下 WebApi 构造函数:

public MyController(ILogger<MyController> logger, 
    Channel channel,
    AuditManager auditManager,
    IMediator mediator)
{
    _logger = logger;
    _channel = channel;

    _channel.EventFinished += async (o, e) =>
    {
        await mediator.Publish(new EventFinishedNotification(e);
    };
    
    _auditManager = auditManager;
}

基本上我订阅了 _channel.EventFinished 事件(当我的一个端点被调用时触发)。发生这种情况时,在调用 mediator.Publish 时我得到一个

Cannot access a disposed object. Object name: 'IServiceProvider'.'

异常。有什么想法吗?

好吧,为了其他人的利益,问题似乎是从构造函数初始化的回调中调用调解器发布,就像我正在做的那样。我已经将那部分代码包装在一个单独的服务中,并且工作正常。

它发生了,因为您订阅了从 DI 解析的 IMediator 实例。这里编译器在 += 之后为委托私有化 class 并在其中“缓存”IMediator 的实例。
当您的控制器方法执行结束时,DI 正在处理容器。如果在那之后事件触发,您的方法调用 mediator.Publish,它在内部使用服务定位器模式并尝试从 IServiceProvider 解析所需的服务。因为它已经被处理掉了,所以你捕获了一个异常。 有几种处理方式:

  • 更改其工作方式和体系结构,例如将 classic 事件更改为进程中事件引发。 (当然需要额外的代码)
  • 为此目的创建托管服务,它将正确处理 DI 和生命周期,例如:
public sealed class ChannelHostedService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly Channel _channel;

    // Resolve channel from DI or initialize it in constructor.
    public ChannelHostedService(IServiceProvider serviceProvider, Channel channel)
    {
        _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
        _channel = channel ?? throw new ArgumentNullException(nameof(channel));
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Code for start channel listening.
        
        // Our new subscriber.
        _channel.EventFinished += async (o, e) =>
        {
            // Here we creating new scope from IServiceProvider to handle our event
            using (var scope = _serviceProvider.CreateScope())
            {
                // Mediator resolves from our scope. 
                var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
                await mediator.Publish(new EventFinishedNotification(e);
            }
        };
    }
}

然后在Startup.cs

中的DI容器中注册它
services.AddHostedService<ChannelHostedService>();

在这个例子中,我们在单例 BackgroundService 中注入单例 IServiceProvider,并创建新的服务范围以从 IServiceProvider 执行。显然,在这种情况下,您的 Channel 应该是单例。
我没有看到你的代码,也不知道什么是 Channel 以及你为什么在控制器中初始化订阅。但这种方法适用于订阅,例如当我们想从消息代理读取事件时:RabbitMq、Kafka 等。我们只需在 DI 或后台服务构造函数中初始化一次通道,然后它一直工作到我们的应用程序关闭。
希望对你有帮助。