在 DotNetCore 3.1 Worker Service 中使用自定义 DI 提供程序?

Use custom DI provider in DotNetCore 3.1 Worker Service?

有谁知道如何在 DotNetCore Worker Service 中设置自定义 DI 提供程序(例如 Castle Windsor)?我已经看到有关如何为 ASP.NET 应用程序执行此操作的信息,但所有示例都在谈论修改 Startup.cs,这在 Worker Service 模板中不存在。

在 .NET Core Worker 项目中使用自定义依赖注入容器

Castle Windsor 不支持最新的 .NET Core 版本,因此我将使用 Autofac 作为示例。

当您开始一个新的 worker 项目时,您将拥有一个这样的程序 class

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

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

Host.CreateDefaultBuilder给你一个IHostBuilder,它有扩展或替换DI容器的必要方法。我们正在使用 Autofac,所以让我们安装它:

dotnet add package Autofac.Extensions.DependencyInjection

那就介绍给我们的主人吧。这里ContainerBuilder来自Autofac assembly.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        // use autofac
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        // configure autofac di container
        .ConfigureContainer<ContainerBuilder>((hostContext, container) =>
        {
            // hostcontext gives you access to config & environment
            // hostContext.Configuration.GetSection("ApiKeys");
            // hostContext.HostingEnvironment.IsDevelopment()

            // auto register all classes in the assembly
            container
                .RegisterAssemblyTypes(typeof(Program).Assembly)
                .AsImplementedInterfaces();
        })
        // configure microsoft di container
        .ConfigureServices((hostContext, services) =>
        {
            // let autofac register this service
            // services.AddHostedService<Worker>();
        });

现在让我们创建一个服务。 IJob 这里的接口是不必要的,只是为了展示 Autofac 的功能:

internal interface IJob
{
    Task ExecuteAsync(CancellationToken cancellationToken = default);
}

internal class WarmUpCaches : IJob
{
    private readonly ILogger<WarmUpCaches> _logger;

    public WarmUpCaches(ILogger<WarmUpCaches> logger)
    {
        _logger = logger;
    }

    public async Task ExecuteAsync(CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("Warming up caches");
        var steps = 5;
        while (!cancellationToken.IsCancellationRequested && --steps > 0)
        {
            _logger.LogInformation("working...");
            await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
        }

        _logger.LogInformation("Done");
    }
}

现在让我们在后台服务中使用这个服务:

class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IJob _job;

    public Worker(ILogger<Worker> logger, IJob job)
    {
        _logger = logger;
        _job = job;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await _job.ExecuteAsync(stoppingToken);
    }
}

当我们 运行 应用程序时,它会记录:

info: CustomDiExample.WarmUpCaches[0]
      Warming up caches
info: CustomDiExample.WarmUpCaches[0]
      working...
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\...\CustomDiExample
info: CustomDiExample.WarmUpCaches[0]
      working...
info: CustomDiExample.WarmUpCaches[0]
      working...
info: CustomDiExample.WarmUpCaches[0]
      working...
info: CustomDiExample.WarmUpCaches[0]
      Done

在不使用后台服务的情况下使用服务

如果应用程序不需要继续工作,我们可以不用 BackgroundService

我们需要修改Main方法并公开IHost实例。我们可以使用 IHost.Services.

从容器中解析任何服务
public static async Task Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    // stop the application with Ctrl+C, SIGINT, SIGTERM
    var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
    var cancellationToken = lifetime.ApplicationStopping;

    var job = host.Services.GetRequiredService<IJob>();
    await job.ExecuteAsync(cancellationToken);
}

参考资料