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

N层架构Telegram Bot出现无法访问已释放DbContext实例错误求助

问题根本原因

你遇到的上下文已释放错误,是下面几个配置和代码的问题叠加导致的:

  • 生命周期不匹配:ApplicationContext通过AddDbContext注册时默认是Scoped生命周期,但是你把IUnitOfWork、仓储都注册成了Transient,且你的UnitOfWork.Dispose方法主动释放了DI容器托管的ApplicationContext,你手动释放之后,容器后续再尝试操作这个上下文时就会报错
  • 异步方法未正确等待:SaveArticles是异步方法,如果调用链上没有全程加await,会导致数据库操作还没执行完,请求结束后Scoped的上下文就被DI容器释放,等异步操作走到AddAsync/Complete时上下文已经失效
  • 异步同步混用:你用了异步的AddAsync添加数据,但是后续提交调用的是同步的SaveChanges,容易在异步上下文切换时触发提前释放问题

具体修复步骤

1. 移除UnitOfWork中手动释放上下文的逻辑

DI容器会自动管理Scoped服务的释放,不需要你主动释放注入的ApplicationContext,直接删除UnitOfWork中的Dispose实现即可:

// 删掉这行代码
public void Dispose() => _context.Dispose();

如果一定要保留IDisposable实现,仅释放你自己手动创建的非DI托管资源即可,不要触碰DI注入的上下文。

2. 统一异步操作,给UoW新增异步提交方法

避免异步同步混用,同时适配EF Core的异步操作逻辑:

// IUnitOfWork接口新增异步提交方法
public interface IUnitOfWork : IDisposable
{
    INewsRepository News { get; }
    IUserRepository Users { get; }
    int Complete();
    // 新增
    Task<int> CompleteAsync(CancellationToken cancellationToken = default);
}

// UnitOfWork实现异步提交
public async Task<int> CompleteAsync(CancellationToken cancellationToken = default)
{
    return await _context.SaveChangesAsync(cancellationToken);
}

修改SaveArticles方法用异步提交:

private async Task SaveArticles(IEnumerable<NewsDTO> articlesDTO)
{            
    var articles = _mapper.Map<IEnumerable<NewsDTO>, IEnumerable<News>>(articlesDTO);
    await _unitOfWork.News.AddAsync(articles.First());
    // 替换同步提交为异步
    await _unitOfWork.CompleteAsync();
}

3. 修正依赖注入生命周期匹配

把UoW和仓储的生命周期从Transient改成Scoped,和DbContext的生命周期对齐:

public static class DALInjection
{
    public static void Injection(IServiceCollection services)
    {
        // 全部改为Scoped
        services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
        services.AddScoped<IUserRepository, UserRepository.UserRepository>();
        services.AddScoped<INewsRepository, NewsRepository.NewsRepository>();
        services.AddScoped<IUnitOfWork, UnitOfWork.UnitOfWork>();
    }
}

4. 检查调用链全程加await

检查ParserService中MakeHtmlRequest方法调用SaveArticles的位置必须加await:

// 示例:MakeHtmlRequest内调用SaveArticles必须加await
public async Task<IEnumerable<NewsDTO>> MakeHtmlRequest(int offset, int count)
{
    // 爬取逻辑省略
    await SaveArticles(articlesDTO); // 这里的await不能丢
    return articlesDTO;
}

可选修复(长轮询Bot适用)

如果你的Telegram Bot是用长轮询而非Webhook接收更新,处理逻辑不在HTTP请求的Scoped上下文内,需要用IServiceScopeFactory手动创建Scope获取服务,保证上下文在处理期间不会被释放:

// 处理更新的逻辑示例
private readonly IServiceScopeFactory _scopeFactory;
// 构造函数注入IServiceScopeFactory

private async Task HandleUpdateAsync(Update update, CancellationToken cancellationToken)
{
    using var scope = _scopeFactory.CreateScope();
    var parser = scope.ServiceProvider.GetRequiredService<IParser>();
    // 后续用parser执行业务逻辑即可
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 06:27:00