在 BackgroundService 中使用作用域服务

在 BackgroundService 中使用作用域服务

通过 AddHostedService 注册 IHostedService 实现时,该服务的生命周期是单例。但某些后台工作需要依赖作用域服务。有关差异,请参阅服务生命周期。

本教程将介绍:

“.NET 中的工作者服务”示例源代码可从示例浏览器获取。

先决条件

创建新项目

在 Visual Studio 中选择“文件 → 新建 → 项目”,搜索“辅助角色服务”(Worker Service)并选择相应模板。

使用 .NET CLI 时,在工作目录打开终端,并将 <Project.Name> 替换为所需项目名称:

dotnet new worker --name <Project.Name>

详情见 dotnet new worker。

Visual Studio Code 用户也可以从集成终端运行 .NET CLI 命令。

创建作用域服务

在 BackgroundService 中使用作用域服务,需要通过 CreateAsyncScope 创建异步作用域。默认情况下,托管服务不会自动获得作用域。

具体后台任务逻辑放在作用域服务中。先定义接口:

namespace App.ScopedService;

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

接口仅包含 DoWorkAsync 方法。新建 DefaultScopedProcessingService.cs,添加实现:

namespace App.ScopedService;

public sealed class DefaultScopedProcessingService(
    ILogger<DefaultScopedProcessingService> logger) : IScopedProcessingService
{
    private readonly string _instanceId = Guid.NewGuid().ToString();

    public Task DoWorkAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{ServiceName} doing work, instance ID: {Id}",
            nameof(DefaultScopedProcessingService),
            _instanceId);

        return Task.CompletedTask;
    }
}

该类使用主构造函数注入 ILogger。DoWorkAsync 返回 Task,接受 CancellationToken,并记录实例标识符。每次类实例化时,都会分配新的 _instanceId。

重写 Worker 类

将模板中的 Worker 类替换为以下内容,并将文件重命名为 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);

        while (!stoppingToken.IsCancellationRequested)
        {
            await using AsyncServiceScope scope = serviceScopeFactory.CreateAsyncScope();

            IScopedProcessingService scopedProcessingService =
                scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();

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

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

        await base.StopAsync(stoppingToken);
    }
}

只要 stoppingToken 尚未取消,循环就通过 IServiceScopeFactory 创建异步作用域,从 AsyncServiceScope 的服务提供程序解析 IScopedProcessingService,再等待 DoWorkAsync,并将停止令牌传入该方法。随后等待十秒,继续下一轮。

每一轮都会创建新的 DefaultScopedProcessingService 实例并记录其标识符;await using 保证该轮作用域被异步释放。

将 Program.cs 替换为:

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,处理服务使用 AddScoped。详情见 .NET 依赖项注入。

验证服务行为

在 Visual Studio 中按 F5,或选择“调试 → 开始调试”。使用 CLI 时,在项目目录执行:

dotnet run

有关该命令,请参阅 dotnet run。

让程序运行一段时间,产生多次 DoWorkAsync 调用。每一轮都会记录新的实例标识符。原文展示了类似如下的日志:

info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is running.
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService doing work, instance ID: 8986a86f-b444-4139-b9ea-587daae4a6dd
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 doing work, instance ID: 07a4a760-8e5a-4c0a-9e73-fcb2f93157d3
info: App.ScopedService.DefaultScopedProcessingService[0]
      DefaultScopedProcessingService doing work, instance ID: c847f432-acca-47ee-8720-1030859ce354
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.ScopedService.ScopedBackgroundService[0]
      ScopedBackgroundService is stopping.

在 Visual Studio 中选择“调试 → 停止调试”,或在控制台按 Ctrl+C 发出取消信号。

另请参阅


来源:Microsoft Learn,在 BackgroundService 中使用作用域服务,原页面更新日期:2026-06-22。中文整理;原文与示例归 Microsoft 及其贡献者所有。文档仓库的许可见 dotnet/docs LICENSE。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容