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

继承后Hot Chocolate生命周期变更致DbContext已释放异常

问题描述

我有一个遗留REST API的Repository,想复用它到新的Hot Chocolate GraphQL API中。按照池化上下文的最佳实践配置后,未实现异步释放时一切正常,但添加IAsyncDisposable接口和DisposeAsync方法后,第二次请求触发以下异常:

System.ObjectDisposedException: 'Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
ObjectDisposed_ObjectName_Name'

异常仅在第二次查询后触发,单请求内链式执行多个Mutation正常,且无async void代码。复刻官方示例项目运行正常,推测问题出在Repository实现上。

相关代码和DI配置见原文。

问题原因及解决方案

核心原因

  1. 手动管理池化上下文生命周期违规:你在HotRepository构造中手动调用contextFactory.CreateDbContext()创建上下文,又在DisposeAsync中手动释放。但池化上下文的实例回收应由IDbContextFactory统一管理,手动释放会将实例标记为不可用,当工厂复用该实例时就会触发异常。
  2. Repository生命周期未按预期生效:虽然注册了Transient,但graphQlServerBuilder.RegisterService<IProductRepository>()默认生命周期可能不是Transient,导致Repository实例被复用。第一次请求后上下文被手动释放,第二次请求复用同一个Repository时就会访问已释放的上下文。

解决方案

1. 移除手动的异步释放逻辑

池化上下文的创建和回收由DI容器与IDbContextFactory负责,不需要手动调用DisposeAsync。修改HotRepository:

public class HotRepository : ProductRepository
{
    // 直接注入Hot Chocolate管理的池化上下文,而非工厂
    public HotRepository(ProductDbContext context, IMapper mapper) 
        : base(context, mapper)
    {
    }

    // 移除IAsyncDisposable接口及DisposeAsync方法
}

2. 确保Repository生命周期为Transient

在DI注册时明确指定Repository的服务生命周期:

builder.Services.AddTransient<IProductRepository, HotRepository>();
// 明确告知Hot Chocolate使用Transient生命周期
graphQlServerBuilder.RegisterService<IProductRepository>(ServiceKind.Transient);

3. 调整Repository的上下文使用方式(可选)

如果需要保留基类ProductRepository的原有结构,可改为注入IDbContextFactory,每次操作时创建上下文并使用await using包裹(仅异步方法):

public class ProductRepository : IProductRepository
{
    private readonly IDbContextFactory<ProductDbContext> _contextFactory;
    private readonly IMapper _mapper;

    public ProductRepository(IDbContextFactory<ProductDbContext> contextFactory, IMapper mapper)
    {
        _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory));
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
    }

    public async Task<bool> ProductExistsAsync(string name)
    {
        await using var context = _contextFactory.CreateDbContext();
        return await context.Products.AnyAsync(product => product.Name == name);
    }

    // 同步方法建议改为异步,避免阻塞池化上下文
}

额外说明

  • 尽量使用异步方法操作数据库,同步操作可能影响池化上下文的回收效率。
  • Hot Chocolate的RegisterDbContext<ProductDbContext>(DbContextKind.Pooled)已经正确配置了池化上下文的生命周期,无需手动干预。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:00:12