.NET Worker 服务中的构造函数注入

Constructor Injection in .NET Worker Service

我花了几个月的时间使用 ASP.NET Core MVC 编写代码。现在我正在尝试工作人员服务,但遇到了依赖注入的一些问题。

我可以在 Program.cs 中注册服务并在 Worker.cs 中使用它(见下文)。当您需要像这样的 class 结构中的服务时,我的问题就出现了:

当然,我可以将服务向下传递到整个层次结构,但在这种情况下,我看不到服务的价值。 似乎我只能在工作服务级别上使用依赖注入。在 MVC 中,您可以简单地注册一个服务并将其作为参数传递给 Controller 的构造函数。这里的优点是您永远不必像 Worker.cs 级别那样显式调用构造函数。 我在尝试访问连接字符串和设置时遇到了这个问题,但我想这同样适用于记录器。

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
            services.AddTransient<ITestService,TestService>();
        });

Worker.cs

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    public readonly ITestService TestServ;

    public Worker(ILogger<Worker> logger, ITestService test)
    {
        _logger = logger;
        TestServ = test;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            //this works
            _logger.LogInformation("TestService: " + TestServ.GetData());
            
            //is this necessary?
            TestClassServ test = new TestClassServ(TestServ);
            
            _logger.LogInformation("TestService: " + test.GetData());

            await Task.Delay(1000, stoppingToken);
        }
    }
}

Class 和服务

public interface ITestService
{
    string GetData();
}

public class TestService : ITestService
{
    public const string teststring = "service";

    public string GetData()
    {
        return teststring;
    }
}

public class TestClassServ
{
    public ITestService TestServ { get; set; }

    public TestClassServ(ITestService testServ)
    {
        TestServ = testServ;
    }

    public string GetData()
    {
        return TestServ.GetData();
    }
}

经过数周的谷歌搜索和尝试,我决定向您寻求帮助:

  1. 甚至可以在 class 层次结构的较低级别注入服务而不从顶部向下传递服务吗?
  2. 你会怎么做?
  3. 我的问题是否源于程序的架构?

It seems like I can use Dependency Injection only on the level of the worker service.

该框架当然能够创建包含深层对象图的工作器 类,因此当然可以将依赖项注入多个层次;即使与工人 类.

一起工作

但这里有一个问题,即 Worker 类 仅由框架解析一次,基本上使它们成为单例。这意味着,即使您可以向它们注入依赖项,它的直接和间接依赖项也不应该是 Scoped 依赖项。

我假设这就是您正在努力解决的问题,因为在大多数情况下,为了让工作人员做任何有用的事情,它需要与数据库对话。这通常意味着使用 Entity Framework,并且其 DbContext 始终是 Scoped。在 Singleton 中注入它是 a really bad idea. The DbContext becomes a Captive Dependency.

这意味着,您必须在应用程序的生命周期内从容器中解析它们,而不是将依赖项注入到 worker 的构造函数中。这将 Worker 耦合到 DI 容器,但是当您使 Worker 成为 Composition Root.

的一部分时,这不是问题

不过,这确实需要您在解析服务之前创建一个新范围。这是一个例子:

public class Worker : BackgroundService
{
    // NOTE: Logger can be safely injected; loggers are always singletons
    private readonly ILogger<Worker> _logger;
    private readonly IServiceProvider _provider;

    public Worker(ILogger<Worker> logger, IServiceProvider provider)
    {
        _logger = logger;
        _provider = provider;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await using (var scope = _provider.CreateScope())
            {
                var service = scope.GetRequiredService<ITestService>();
                service.GoDoSomethingUseful();
            }
        
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Is my problem rooted in the architecture of the program?

不,就您的代码示例所显示的而言不是。只要您继续访问 DI 容器(或其 IServiceProvider 抽象)在您的组合根中 ,就可以了。