在 BackgroundService 中使用作用域服务
通过 AddHostedService 注册 IHostedService 实现时,该服务的生命周期是单例。但某些后台工作需要依赖作用域服务。有关差异,请参阅服务生命周期。
本教程将介绍:
- 在单例
BackgroundService中正确解析作用域依赖。 - 将具体工作委托给作用域服务。
- 重写
BackgroundService.StopAsync(CancellationToken)。
“.NET 中的工作者服务”示例源代码可从示例浏览器获取。
先决条件
- .NET 8.0 SDK 或更高版本。
- .NET IDE,例如 Visual Studio。
创建新项目
在 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。











暂无评论内容