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

HotChocolate中依赖注入DbContext出现已释放实例访问错误

问题描述

我使用GraphQL库HotChocolate,相关实现代码如下:

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddGraphQLServer()
    .AddAuthorization()
    .AddQueryType<ProductQuery>();


builder.Services.AddDbContext<DpcDatabaseContext>();
// ...

var app = builder.Build();

app.UseRouting();

app.UseHttpsRedirection();

app.UseAuthentication();

app.UseAuthorization();

app.MapGraphQL();

app.Run();

通过构造函数注入获取数据库上下文:

public DpcProductDatabaseService(DpcDatabaseContext dpcDatabaseContext)
{
    _dpcDatabaseContext = dpcDatabaseContext;
  
}

获取产品的代码:

var res = await _dpcDatabaseContext.Products.FirstOrDefaultAsync(z => z.Id == 1, cancellationToken);

运行时抛出以下错误:

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.
Object name: 'DpcDatabaseContext'.
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_Model()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, LambdaExpression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, Expression`1 predicate, CancellationToken cancellationToken)
   at DpcMicroService.Services.DpcProductDatabaseService.GetProductsByFilterFromDbInternalAsync(FilterInput filter, DpcDatabaseContextBase ctx, CancellationToken cancellationToken) in C:\Beeline\DpcMicroService\DpcMicroService\DpcMicroService\Services\DpcProductDatabaseService.cs:line 86

我尝试将DI配置改为:

builder.Services.AddDbContext<DpcDatabaseContext>(ServiceLifetime.Transient);

但没有效果。手动使用new创建上下文可以正常运行,请问我哪里出错了?

问题分析与解决方案

核心原因

问题出在服务生命周期不匹配:

  • 默认情况下,AddDbContext注册的DbContext是Scoped生命周期,即每个请求创建一个实例,请求结束后自动释放。
  • 如果你的DpcProductDatabaseService被注册为Singleton(单例),那么它会持有这个Scoped的DbContext实例。当请求结束后,DbContext被DI容器释放,但Singleton服务还会继续复用这个已释放的上下文,就会抛出ObjectDisposedException。

解决办法

1. 检查并修正服务的生命周期

确保DpcProductDatabaseService的生命周期是Scoped或Transient,不要使用Singleton。在注册服务时添加对应配置:

// 注册为Scoped(推荐,和DbContext生命周期匹配)
builder.Services.AddScoped<DpcProductDatabaseService>();
// 或者注册为Transient
// builder.Services.AddTransient<DpcProductDatabaseService>();

2. 使用IDbContextFactory创建上下文(更可靠的方案)

如果服务必须是Singleton,或者想彻底避免生命周期问题,可以使用IDbContextFactory来按需创建DbContext实例,确保每次使用的都是有效上下文:

  • 首先注册DbContext工厂:
builder.Services.AddDbContextFactory<DpcDatabaseContext>(options =>
{
    // 配置你的数据库连接,比如使用SqlServer
    options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString"));
});
  • 修改服务的构造函数,注入IDbContextFactory<DpcDatabaseContext>:
private readonly IDbContextFactory<DpcDatabaseContext> _dbContextFactory;

public DpcProductDatabaseService(IDbContextFactory<DpcDatabaseContext> dbContextFactory)
{
    _dbContextFactory = dbContextFactory;
}
  • 在获取产品的方法中创建并使用上下文:
using var ctx = _dbContextFactory.CreateDbContext();
var res = await ctx.Products.FirstOrDefaultAsync(z => z.Id == 1, cancellationToken);

这种方式会在每次使用时创建新的DbContext实例,使用完后自动释放,完全避免上下文被提前释放的问题。

3. 不要随意修改DbContext的生命周期

将DbContext改为Transient并不能解决根本问题,因为如果服务是Singleton,依然会持有Transient的DbContext实例,当它被释放后(比如DI容器清理),同样会报错。保持DbContext的默认Scoped生命周期是Web应用的最佳实践。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:53:16