使用 IServiceProvider 从单例实例创建范围

mat*_*rai 3 c# dependency-injection .net-core asp.net-core

假设我有自己的类QueueListener<TService, TPayload>继承自BackgroundService. 它打开 TCP 连接并侦听传入消息。在每条消息上,我想初始化TService类型的服务并将其从 JSON 实例反序列化传递TPayload给它。TService将被注册为 Transient,因此这意味着作为有效负载的处理程序必须是轻量级和无状态的(在我当前的任务中)。为此,我将注入IServiceProvidermy 的构造函数QueueListener,并在它收到的每条消息上创建一个范围。这听起来像是一个计划还是我设计过度了?TService我也想避免单例。

文件

从单例解析范围服务是危险的。它可能会导致服务在处理后续请求时出现不正确的状态。

但我不完全确定这意味着什么。无法注入作用域服务,BackgroundService因为它具有单例生命周期。他们是否警告我停止做这样的事情?

更新#1

我解释了为什么我要为每条消息创建范围。其背后的想法是防止侦听器被消息处理阻塞,并为其他开发人员提供创建自己的处理程序并对收到的消息执行一些操作的可能性。例如,其他开发人员可以在处理时创建数据库连接,我希望在处理完成时关闭并释放它。

mtk*_*nko 5

注册TService为作用域并为每条消息创建一个新作用域。然后TService从创建的范围解析。只需阅读在后台任务中使用范围服务

你可以这样写:

services.AddHostedService<MyBackgroundService>();
services.AddScoped<IScopedServicePerMessage, ScopedServicePerMessage>();

...

public class MyBackgroundService : BackgroundService
{
    private readonly IServiceProvider _sp;
    public MyBackgroundService(IServiceProvider sp)
    {
        _sp = sp;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        DoWork(stoppingToken);
        return Task.CompletedTask;
    }

    private void DoWork(CancellationToken stoppingToken)
    {
        while(true)
        {
            var msg = GetNextMessage();
            using (var scope = _sp.CreateScope())
            {
                var servicePerMessage = scope.ServiceProvider.GetRequiredService<IScopedServicePerMessage>();
                servicePerMessage.Handle(msg);
            }
        }
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

关于这一点:

从单例解析范围服务是危险的。它可能会导致服务在处理后续请求时出现不正确的状态。

这是关于将作用域服务(例如 ef core dbcontext)直接注入到单例中的情况。这不是你的情况。