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

C# HTTP API后台更新数据库时DBContext已释放问题求助

问题根源

你的代码核心问题是依赖范围(Scope)被提前释放:

  • 你用Task.Factory.StartNew启动了一个异步委托,但这个委托是async void类型,Task.Factory.StartNew返回的task1仅会跟踪到第一个await之前的同步代码完成,后续异步操作还在执行时,ContinueWith就已经触发并释放了executeScope。
  • 当后台异步操作执行到await repository.UpDateDBmodel(att)时,repository依赖的DBContext已经随着executeScope的释放被销毁,因此抛出ObjectDisposedException。
修复方案

1. 正确管理依赖范围的生命周期

使用using语句自动管理Scope的释放,确保异步操作全部完成后才释放Scope,而不是手动调用ContinueWith。

2. 避免async void和Task.Factory.StartNew

  • 用async Task替代async void,确保异步操作的完成状态能被正确跟踪。
  • 用Task.Run替代Task.Factory.StartNew,它对异步委托的支持更友好。

修改后的完整代码

主方法调整

public async Task<DBmodel> GetAttachmentsListById(Guid Id, bool RequestSync)
{
    var target = await Repository.GetDBmodelById(Id);
    if (target != null)
    {
        if (RequestSync)
        {
            // 用_ = 避免fire-and-forget的编译器警告,同时不等待后台任务完成
            _ = SyncAttachment(target);
        }
        return target; // 原代码里return DBmodel是笔误,应该返回target
    }
    
    return null!;
}

SyncAttachment方法修复

// 改为async Task,避免async void
public async Task SyncAttachment(DBmodel target)
{
    // 使用using自动管理Scope生命周期,异步操作完成后才释放
    using var executeScope = _serviceProvider.CreateScope();
    var serviceProvider = executeScope.ServiceProvider;
    var repository = serviceProvider.GetRequiredService<Repository>();

    ExternalClient client = new ExternalClient();
    var externalResult = await client.GetExternalInformation(); // 补全外部调用逻辑
    if (NeedUpdate(externalResult)) // 补全更新判断逻辑
    {
        foreach (var x in externalResult)
        {
            // 修正原代码的赋值错误,应该是映射属性而非直接赋值对象
            var model = new DBmodel
            {
                // 这里填充从x获取的属性,比如:
                Id = x.Id,
                Name = x.Name
                // 其他需要更新的字段
            };
            await repository.UpDateDBmodel(model); // 修正变量名,原代码里的att应该是model
        }
    }
}
额外注意事项
  • 异常处理:后台任务如果抛出异常,默认会被吞噬,建议在SyncAttachment里添加try-catch块,记录日志或处理异常。
  • 构造注入ServiceProvider:确保_serviceProvider是通过构造函数注入的,而不是在方法内赋值,符合依赖注入的最佳实践。
  • 避免直接传递DB实体:target是从数据库查询出来的实体,在后台任务中如果不需要修改它,建议传递必要的ID或DTO,而非整个实体,避免潜在的跟踪问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:55:06