BackgroundService 内使用作用域服务

当使用任意 AddHostedService 扩展方法注册 IHostedService 的实现时,该服务被注册为单一实例。 在某些情况下,你可能想要依赖于作用域服务。 有关详细信息,请参阅 .NET 服务生存期中的依赖关系注入

在本教程中,你将了解如何执行以下操作:

提示

所有“.NET 中的辅助角色”示例源代码都可以在示例浏览器中下载。 有关详细信息,请参阅浏览代码示例:.NET 中的辅助角色

先决条件

创建新项目

若要使用 Visual Studio 创建新的辅助角色服务项目,请选择“文件”“新建”“项目...”。从“创建新项目”对话框搜索“辅助角色服务”,并选择辅助角色服务模板。 如果你想要使用 .NET CLI,请在工作目录中打开你最喜欢的终端。 运行 dotnet new 命令,将 <Project.Name> 替换为所需的项目名称。

dotnet new worker --name <Project.Name>

有关 .NET CLI 新建辅助角色服务项目命令的详细信息,请参阅 dotnet new 辅助角色

提示

如果使用 Visual Studio Code,则可以从集成终端运行 .NET CLI 命令。 有关详细信息,请参阅 Visual Studio Code:集成终端

创建作用域服务

要在 BackgroundService 中使用有作用域的服务,请创建一个作用域。 默认情况下,不会为托管服务创建作用域。 作用域后台服务包含后台任务的逻辑。

namespace App.ScopedService;

public interface IScopedProcessingService
{
    Task DoWorkAsync(CancellationToken stoppingToken);
}

前面的接口定义了一个 DoWorkAsync 方法。 定义默认实现:

  • 服务是异步的。 DoWorkAsync 方法返回 Task。 出于演示目的,在 DoWorkAsync 方法中等待 10 秒的延迟。
  • ILogger 注入到服务中:
namespace App.ScopedService;

public sealed class DefaultScopedProcessingService(
    ILogger<DefaultScopedProcessingService> logger) : IScopedProcessingService
{
    private int _executionCount;

    public async Task DoWorkAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            ++ _executionCount;

            logger.LogInformation(
                "{ServiceName} working, execution count: {Count}",
                nameof(DefaultScopedProcessingService),
                _executionCount);

            await Task.Delay(10_000, stoppingToken);
        }
    }
}

托管服务创建一个作用域来解决作用域后台服务以调用其 DoWorkAsync 方法。 DoWorkAsync 返回 ExecuteAsync 等待的 Task

重写辅助角色类

将现有 Worker 类替换为以下 C# 代码,并将该文件重命名为“ScopedBackgroundService.cs”:

namespace App.ScopedService;

public sealed class ScopedBackgroundService(
    IServiceScopeFactory serviceScopeFactory,
    ILogger<ScopedBackgroundService> logger) : BackgroundService
{
    private const string ClassName = nameof(ScopedBackgroundService);

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Name} is running.", ClassName);

        await DoWorkAsync(stoppingToken);
    }

    private async Task DoWorkAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Name} is working.", ClassName);

        using (IServiceScope scope = serviceScopeFactory.CreateScope())
        {
            IScopedProcessingService scopedProcessingService =
                scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();

            await scopedProcessingService.DoWorkAsync(stoppingToken);
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Name} is stopping.", ClassName);

        await base.StopAsync(stoppingToken);
    }
}

在前面的代码中,将创建一个显式作用域,并通过依赖关系注入服务范围工厂解析 IScopedProcessingService 实现。 解析后的服务实例是有作用域的,它的 DoWorkAsync 方法正在等待。

将模板 Program.cs 文件内容替换为以下 C# 代码:

using App.ScopedService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<ScopedBackgroundService>();
builder.Services.AddScoped<IScopedProcessingService, DefaultScopedProcessingService>();

IHost host = builder.Build();
host.Run();

服务是在(Program.cs)中注册的。 已使用 AddHostedService 扩展方法注册托管服务。

有关注册服务的详细信息,请参阅 .NET 中的依赖关系注入

验证服务功能

若要从 Visual Studio 运行应用程序,请选择 F5 或选择“调试”>“开始调试”菜单选项。 如果使用的是 .NET CLI,请从工作目录运行 dotnet run 命令:

dotnet run

有关 .NET CLI run 命令的详细信息,请参阅 dotnet run

让应用程序运行一段时间,以生成多个执行计数增量。 你将看到与下面类似的输出:

info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is running.
info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is working.
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 1
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: .\scoped-service
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 2
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 3
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService working, execution count: 4
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is stopping.

如果从 Visual Studio 内部运行应用程序,请选择“调试”>“停止调试...” 。或者,从控制台窗口中选择“Ctrl” + “C”,以发送取消信号。

另请参阅