【问题标题】:EF Core DbContext ObjectDisposedExceptionEF Core DbContext ObjectDisposedException
【发布时间】:2019-11-05 23:03:17
【问题描述】:

拥有 EF core 2.2 和 .net core 2.2 我正在努力解决 ObjectDisposedException 问题,例如: herehere

几个事实:

  • 我的所有服务都注册为 Transient,与使用 AddDbContext() 的 DbContext 相同
  • 使用DI注入DbContext实例
  • 堆栈跟踪中提到的所有函数都是async/await

我觉得我在这里遗漏了一些明显的东西,但我已经花了 2-3 天没有任何运气

堆栈跟踪:

Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware [8] - An unhandled exception has occurred while executing the request. System.ObjectDisposedException: Cannot access a disposed object. A common cause of this error is disposing a context 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, or wrapping the context 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: 'BaseContext'.
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies.get_QueryProvider()
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ParameterExtractingExpressionVisitor..ctor(IEvaluatableExpressionFilter evaluatableExpressionFilter, IParameterValues parameterValues, IDiagnosticsLogger`1 logger, DbContext context, Boolean parameterize, Boolean generateContextAccessors)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryModelGenerator.ExtractParameters(IDiagnosticsLogger`1 logger, Expression query, IParameterValues parameterValues, Boolean parameterize, Boolean generateContextAccessors)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
   at System.Linq.AsyncEnumerable.Aggregate_[TSource,TAccumulate,TResult](IAsyncEnumerable`1 source, TAccumulate seed, Func`3 accumulator, Func`2 resultSelector, CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Aggregate.cs:line 118
   at Sample.Infrastructure.Tasks.TagEventTypeTasks.GetAllAsync() in ~root\Sample.API\Sample.Infrastructure\Tasks\TagEventTypeTasks.cs:line 24
   at Sample.API.Helpers.GraphHelpers.GetAllTagEventTypesWithCacheAsync() in ~root\Sample.API\Sample.API\Helpers\GraphHelpers.cs:line 45
   at Sample.API.Controllers.GraphController.GetTagEventTypeTimesAsync() in ~root\Sample.API\Sample.API\Controllers\GraphController.cs:line 59
   at lambda_method(Closure , Object )
   at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()
   at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)

为了完全透明,以下是堆栈跟踪中提到的方法:

GraphController.cs:

[HttpGet("tagEventTypeTimes")]
public async Task<ActionResult<List<TagEventTypeResultDto>>> GetTagEventTypeTimesAsync()
{
    return await _graphHelpers.GetAllTagEventTypesWithCacheAsync();
}

GraphHelpers.cs:

public async Task<List<TagEventType>> GetAllTagEventTypesWithCacheAsync()
{
    string tagEventTypesKey = "tagEventTypes";
    List<TagEventType> tagEventTypes;
    if (!_cache.TryGetValue<List<TagEventType>>(tagEventTypesKey, out tagEventTypes))
    {
        tagEventTypes = await _tagEventTypeTasks.GetAllAsync();
        _cache.Set<List<TagEventType>>(tagEventTypesKey, tagEventTypes, _memCacheOptions);
    }

    return tagEventTypes;
}

TagEventTypeTasks.cs:

public class TagEventTypeTasks : ITagEventTypeTasks
{
    private readonly BaseContext _context;

    public TagEventTypeTasks(BaseContext context)
    {
        _context = context;
    }

    public async Task<List<TagEventType>> GetAllAsync()
    {
        return await _context.TagEventType.OrderByDescending(x => x.Id).AsNoTracking().ToListAsync();
    }
}

BaseContext.cs:

public class BaseContext : DbContext
{
    private readonly ILoggerFactory _logger;

    public BaseContext(DbContextOptions<BaseContext> options, ILoggerFactory logger) : base(options)
    {
        _logger = logger;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseLoggerFactory(_logger);
    }

    public DbSet<Patient> Patient { get; set; }
    public DbSet<Room> Room { get; set; }
    public DbSet<Tag> Tag { get; set; }
    public DbSet<TagEvent> TagEvent { get; set; }
    public DbSet<TagEventType> TagEventType { get; set; }
    public DbSet<TagLocation> TagLocation { get; set; }
    public DbSet<TagRegistration> TagRegistration { get; set; }
    public DbSet<User> User { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.ApplyConfiguration(new PatientConfiguration());
        builder.ApplyConfiguration(new TagConfiguration());
        builder.ApplyConfiguration(new TagRegistrationConfiguration());
        builder.ApplyConfiguration(new TagEventConfiguration());
        builder.ApplyConfiguration(new TagEventTypeConfiguration());
        builder.ApplyConfiguration(new TagLocationConfiguration());
        builder.ApplyConfiguration(new RoomConfiguration());
    }
}

更新: 添加启动相关代码

services.AddDbToServices(Configuration.GetConnectionString("DefaultConnection"));

public static void AddDbToServices(this IServiceCollection services, string connectionString)
{
    services.AddDbContext<BaseContext>(options => options.UseFirebird(connectionString), ServiceLifetime.Transient);
}

更新2

添加了整个 TagEventTypeTasks

UPDATE3

添加的服务注册 我正在使用这个 lib 但是我尝试使用 AddTransient() 手动注册所有服务 -> 也没有帮助

var assembliesToScan = new[]
{
    Assembly.GetExecutingAssembly(),
    Assembly.GetAssembly(typeof(Patient)),
    Assembly.GetAssembly(typeof(PatientTasks))
};

services
    .RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
    .AsPublicImplementedInterfaces(ServiceLifetime.Transient);

【问题讨论】:

  • 如您引用的答案之一中所述,是否有任何其他方法被定义为async void?也许在_cache.TryGetValue
  • _cache 是 IMemoryCache 的类型,TryGetValue() 返回 bool。我在整个解决方案中搜索了 async void,结果为 0
  • 我建议更改 BaseContext 以覆盖 docs.microsoft.com/en-us/dotnet/api/… 。在里面放一个断点。然后查看断点何时被命中(并查找堆栈跟踪)。这可能有助于缩小它比预期更早被处置的原因。
  • Rethrow 在堆栈跟踪中让我认为可能会引发另一个异常 - 但只是一个猜测。
  • 好的,我认为_graphHelpers 处理了上下文,下次执行该操作时,控制器将使用非瞬态_graphHelpers 保留旧上下文。

标签: c# asp.net-core entity-framework-core


【解决方案1】:

这看起来像是依赖注入问题。

我认为_graphHelpers 会释放上下文,下次执行操作时,控制器会使用非瞬态_graphHelpers,它会保留旧的、已释放的上下文。

它甚至可能是在_graphHelpers 上调用Dispose() 的控制器。


一些 DI 示例

这是来自Get Started with ASP.NET Core and Entity Framework 6的设置

(...)
services.AddScoped<SchoolContext>(_ => new SchoolContext(Configuration.GetConnectionString("DefaultConnection")

Tutorial: Get started with EF Core in an ASP.NET MVC web app 使用 AddDbContext 请注意,此示例适用于 .NET Core 2.2,而不是 3.0。

(...)
services.AddDbContext<SchoolContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

这是来自Configuring a DbContext的配置

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<BloggingContext>(options => options.UseSqlite("Data Source=blog.db"));
}

无 DI 解决方案

您可以使用新上下文来获取数据,而不是 _context

public async Task<List<TagEventType>> GetAllAsync()
{
    // If you want DI, then do this only Temporarily, just for a diagnosis.
    using (var db = new InverterContext("connString")) 
    {
       return await db.TagEventType.OrderByDescending(x => x.Id).AsNoTracking().ToListAsync();
    }
}

【讨论】:

  • 是否应该在每次想使用新上下文时实例化它?这是否意味着我不应该使用 DI? // 用启动代码更新了我的问题
  • 您不需要使用DI。可以根据请求实例化上下文。
猜你喜欢
  • 2020-09-23
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 2022-01-16
  • 2021-01-21
相关资源
最近更新 更多