.NET 6多独立后台服务的复用与作用域依赖注入方案
.NET 6 多后台服务的作用域依赖复用方案
1. 封装通用作用域后台服务基类
为了避免每个后台任务重复编写作用域解析逻辑,我们可以封装一个基类,把作用域创建、依赖解析的通用逻辑抽离出来:
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; public abstract class ScopedBackgroundService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger<ScopedBackgroundService> _logger; protected ScopedBackgroundService(IServiceScopeFactory scopeFactory, ILogger<ScopedBackgroundService> logger) { _scopeFactory = scopeFactory; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Scoped background service starting"); while (!stoppingToken.IsCancellationRequested) { try { // 创建独立作用域,所有作用域依赖在此域内解析 using var scope = _scopeFactory.CreateScope(); var scopedServices = scope.ServiceProvider; // 调用具体任务的业务逻辑 await DoWorkAsync(scopedServices, stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "Scoped background service encountered an error"); await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } // 根据任务需求设置执行间隔,也可替换为Quartz等定时调度逻辑 await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } _logger.LogInformation("Scoped background service stopping"); } // 由具体任务实现的业务逻辑方法 protected abstract Task DoWorkAsync(IServiceProvider scopedServices, CancellationToken stoppingToken); }
2. 实现具体的后台任务
每个独立功能的后台任务只需继承上述基类,专注实现自身业务逻辑即可,无需重复处理作用域相关代码:
示例任务1:用户数据同步服务
public class UserSyncWorker : ScopedBackgroundService { public UserSyncWorker(IServiceScopeFactory scopeFactory, ILogger<UserSyncWorker> logger) : base(scopeFactory, logger) { } protected override async Task DoWorkAsync(IServiceProvider scopedServices, CancellationToken stoppingToken) { // 在作用域内解析所需的作用域/瞬态服务 var userService = scopedServices.GetRequiredService<IUserService>(); var dbContext = scopedServices.GetRequiredService<AppDbContext>(); // 执行具体同步逻辑 await userService.SyncRemoteUserDataAsync(dbContext, stoppingToken); } }
示例任务2:订单状态推送服务
public class OrderPushWorker : ScopedBackgroundService { public OrderPushWorker(IServiceScopeFactory scopeFactory, ILogger<OrderPushWorker> logger) : base(scopeFactory, logger) { } protected override async Task DoWorkAsync(IServiceProvider scopedServices, CancellationToken stoppingToken) { var orderService = scopedServices.GetRequiredService<IOrderService>(); var messageQueue = scopedServices.GetRequiredService<IMessageQueue>(); // 执行订单推送逻辑 await orderService.PushPendingOrdersAsync(messageQueue, stoppingToken); } }
3. 注册后台服务
在Program.cs中直接注册各个具体任务即可,它们会自动并行运行:
var builder = WebApplication.CreateBuilder(args); // 先注册所需的作用域依赖(如业务服务、数据库上下文等) builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IOrderService, OrderService>(); builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // 注册各个后台服务 builder.Services.AddHostedService<UserSyncWorker>(); builder.Services.AddHostedService<OrderPushWorker>(); var app = builder.Build(); // ... 其他中间件配置 app.Run();
4. 作用域依赖解析说明
- HostedService默认是单例生命周期,不能直接注入作用域/瞬态服务,否则会导致服务生命周期不匹配,引发内存泄漏问题。
- 通过
IServiceScopeFactory.CreateScope()创建独立作用域,所有依赖都在该作用域内解析,using块结束时作用域会自动释放,确保依赖被正确销毁。 - 每个后台任务的作用域相互独立,并行运行时不会产生干扰。
内容的提问来源于stack exchange,提问作者HariPrasad kasavajjala
相关产品推荐
相关产品推荐

