You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 13:33:05