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

ASP.NET Core 6 Web API中Autofac与BackgroundService协同使用问题排查

问题分析与解决方案

核心原因

控制器注入的repoService属于请求生命周期范围(LifetimeScope),当控制器返回200状态码后,该生命周期范围会被Autofac释放,关联的RepositoryManagerDbContext也随之销毁。后台任务执行时调用绑定了已释放范围的repoService,就会触发ObjectDisposedException。

修复方案

通过在后台任务执行时创建独立的生命周期范围,解析全新的服务实例,确保DbContext未被释放。

1. 修改BackgroundTaskService,注入Autofac根生命周期范围

using Autofac;
using Microsoft.AspNetCore.SignalR;

public class BackgroundTaskService : BackgroundService
{
    private readonly Channel<BackgroundTask> _taskQueue;
    private readonly ILifetimeScope _rootLifetimeScope;

    // 注入Autofac根生命周期范围
    public BackgroundTaskService(ILifetimeScope rootLifetimeScope)
    {
        _rootLifetimeScope = rootLifetimeScope;
        _taskQueue = Channel.CreateUnbounded<BackgroundTask>();
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await foreach (var backgroundTask in _taskQueue.Reader.ReadAllAsync(stoppingToken))
        {
            Console.WriteLine($"Starting task {backgroundTask.Id}");
            
            // 为每个任务创建独立的生命周期范围
            using var taskScope = _rootLifetimeScope.BeginLifetimeScope();
            
            // 根据任务类型执行对应逻辑
            if (backgroundTask is CreateDeveloperEnvironmentTask devTask)
            {
                // 在新范围内解析服务
                var repoService = taskScope.Resolve<IRepositoryService>();
                var hubContext = taskScope.Resolve<IHubContext<SignalRHub>>();
                
                await repoService.CreateDeveloperEnvironment(devTask.Request);
                await hubContext.Clients.All.SendAsync("ReceiveMessage", "CreateTaskRepo", new { Status = "Completed" });
            }
            
            Console.WriteLine($"Completed task {backgroundTask.Id}");
        }
    }

    // 提供特定任务的入队方法
    public async Task EnqueueCreateDeveloperEnvironmentTask(SaveTaskDto request)
    {
        var task = new CreateDeveloperEnvironmentTask { Request = request };
        Console.WriteLine($"Enqueueing task {task.Id}");
        await _taskQueue.Writer.WriteAsync(task);
    }
}

// 抽象任务基类
public abstract class BackgroundTask
{
    public Guid Id { get; } = Guid.NewGuid();
}

// 具体任务类型
public class CreateDeveloperEnvironmentTask : BackgroundTask
{
    public SaveTaskDto Request { get; set; }
}

2. 修改控制器代码,使用新的任务入队方式

[HttpPost]
public async Task<IActionResult> CreateTaskRepo(SaveTaskDto request)
{
    await _backgroundTaskService.EnqueueCreateDeveloperEnvironmentTask(request);
    return Ok();
}

// 移除原CreateDeveloperEnvironmentAndNotify方法,逻辑已移至BackgroundTaskService

3. 保持Autofac注册配置不变

Startup.cs的容器注册无需修改,BackgroundTaskService保持单例(HostedService要求),DbContext的InstancePerLifetimeScope配置保留:

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<BackgroundTaskService>()
           .AsSelf()
           .As<IHostedService>()
           .SingleInstance();
    builder.RegisterModule<DatabaseModule>();
    builder.RegisterModule<GitModule>();
    builder.RegisterModule<ConfigurationModule>();
    builder.RegisterModule<RedmineModule>();
    builder.RegisterModule<ExternalPerspectivModule>();
    builder.RegisterModule<RepositoryModule>();
    builder.RegisterModule<DiskModule>();

    builder.RegisterType<SignalRHub>();
}

DatabaseModule中的DbContext注册:

builder.RegisterType<RepositoryManagerDbContext>().AsSelf().InstancePerLifetimeScope();

修复原理

  • 每个后台任务执行时,从Autofac根范围创建独立的子生命周期范围,在该范围内解析的所有服务(包括DbContext)都属于这个新范围。
  • 任务完成后,子范围被自动释放,DbContext也会被正确回收,既避免了上下文已释放的问题,也符合EF Core的生命周期管理规范。

内容的提问来源于stack exchange,提问作者Kobi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:47:53