简体   繁体   English

无法在.NET Core for Async方法中访问DbContext的已处置对象

[英]Cannot access a disposed object for DbContext in .NET Core for Async method

I am having a weird problem in one of my microservice web api. 我的一个微服务网络api出现了一个奇怪的问题。 My async GET methods throw a Cannot access a disposed object exception for my DbContext except for the very first time they are invoked. 我的异步GET方法为我的DbContext抛出“无法访问已处置的对象”异常,除非是第一次调用它们。 I tried looking online for an answer but nothing worked. 我尝试在网上寻找答案,但没有任何效果。 I made sure my methods are not async void and I await the necessary calls. 我确保我的方法不会异步无效,并且等待必要的调用。 Since my POST and DELETE methods work fine, I am fairly certain that the real culprit is the IMapper instance. 由于我的POST和DELETE方法工作正常,因此我可以确定真正的罪魁祸首是IMapper实例。 I think it might always point to the first instance of the DbContext and that is why the works the first time but not the ones after. 我认为它可能总是指向DbContext的第一个实例,这就是为什么该工程第一次生效而后一次没有生效的原因。 Any help or pointers would be gladly appreciated 任何帮助或指针将不胜感激

Here are some snapshots of the code. 这是代码的一些快照。

Startup.cs Startup.cs

...
// Add AutoMapper
        services.AddAutoMapper(new Assembly[] { typeof(AutoMapperProfile).GetTypeInfo().Assembly });

// Add DbContext using NoSQL Server Provider
services.AddDbContext<ProfileDbContext>(options =>
            options.UseMongoDb(Configuration.GetConnectionString("TeamJobProfilesDatabase")));
...

MyController.cs MyController.cs

    // GET api/profiles
    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<ActionResult<ProfilesListViewModel>> GetAll()
    {
        return Ok(await Mediator.Send(new GetAllProfilesQuery()));
    }

    // GET api/profiles/{id}
    [HttpGet("{id}")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<ProfileViewModel>> Get(int id)
    {
        return Ok(await Mediator.Send(new GetProfileQuery { Id = id }));
    }

GetAllProfilesQueryHandler.cs GetAllProfilesQueryHandler.cs

public class GetAllProfilesQueryHandler : IRequestHandler<GetAllProfilesQuery, ProfilesListViewModel>
{
    private readonly ProfileDbContext _context;
    private readonly IMapper _mapper;

    public GetAllProfilesQueryHandler(ProfileDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    public async Task<ProfilesListViewModel> Handle(GetAllProfilesQuery request, CancellationToken cancellationToken)
    {
        return new ProfilesListViewModel
        {
            Profiles = await _context.Profiles.ProjectTo<ProfileLookupModel>(_mapper.ConfigurationProvider).ToListAsync(cancellationToken)
        };
    }
}

ProfileDbContext.cs ProfileDbContext.cs

[MongoDatabase("profileDb")]
public class ProfileDbContext : DbContext
{
    public ProfileDbContext(DbContextOptions<ProfileDbContext> options)
        : base(options)
    {
    }

    public DbSet<Domain.Entities.Profile> Profiles { get; set; }
}

Exception message 异常消息

{ "error": [ "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.\\r\\nObject name: 'ProfileDbContext'." {“ error”:[“无法访问已处置的对象。此错误的常见原因是处置从依赖项注入中解决的上下文,然后稍后尝试在应用程序中的其他位置使用相同的上下文实例。在对象上调用Dispose()或将上下文包装在using语句中。如果使用依赖项注入,则应让依赖项注入容器来处理上下文实例。\\ r \\ n对象名称:'ProfileDbContext'。” ], "stackTrace": " at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()\\r\\n at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()\\r\\n at Microsoft.EntityFrameworkCore.DbContext.get_ChangeTracker()\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompilationContextFactory.get_TrackQueryResults()\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompilationContextFactory.Create(Boolean async)\\r\\n at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](QueryModel queryModel)\\r\\n at Blueshift.EntityFrameworkCore.MongoDB.Storage.MongoDbDatabase.<>c__DisplayClass11_0 1.<CompileAsyncQuery>b__0(QueryContext queryContext)\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 1.System.Collections.Generic.IAs ],“ stackTrace”:“在Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()\\ r \\ n在Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()\\ r \\ n在Microsoft.EntityFrameworkCore.DbContext.get_ChangeTracker()\\ r \\ n在Microsoft.EntityFrameworkCore.Query.Internal.QueryCompilationContextFactory.get_TrackQueryResults()\\ r \\ n在Microsoft.EntityFrameworkCore.Query.Internal.QueryCompilationContextFactory.Create(布尔异步)\\ r \\ n在Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery [TResult] (QueryModel queryModel)\\ r \\ n在Blueshift.EntityFrameworkCore.MongoDB.Storage.MongoDbDatabase。<> c__DisplayClass11_0 1.<CompileAsyncQuery>b__0(QueryContext queryContext)\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)\\r\\n at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 1.System.Collections.Generic。执行机构 yncEnumerable.GetEnumerator()\\r\\n 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\\r\\n at Profile.Application.Profiles.Queries.GetAllProfiles.GetAllProfilesQueryHandler.Handle(GetAllProfilesQuery request, CancellationToken cancellationToken) in C:\\\\Users\\\\Adam\\\\Repositories\\\\TeamJob\\\\TeamJob\\\\src\\\\Services\\\\Profile\\\\Profile.Application\\\\Profiles\\\\Queries\\\\GetAllProfiles\\\\GetAllProfilesQueryHandler.cs:line 24\\r\\n at MediatR.Pipeline.RequestPostProcessorBehavior 2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate 1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior 2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate 1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior 2.Handle(TRequ yncEnumerable.GetEnumerator(个)\\ r \\ n在System.Linq.AsyncEnumerable.Aggregate_ [TSource,TAccumulate,TResult](IAsyncEnumerable 1 source, TAccumulate seed, Func 3累加器,Func键2 resultSelector, CancellationToken cancellationToken) in D:\\\\a\\\\1\\\\s\\\\Ix.NET\\\\Source\\\\System.Interactive.Async\\\\Aggregate.cs:line 118\\r\\n at Profile.Application.Profiles.Queries.GetAllProfiles.GetAllProfilesQueryHandler.Handle(GetAllProfilesQuery request, CancellationToken cancellationToken) in C:\\\\Users\\\\Adam\\\\Repositories\\\\TeamJob\\\\TeamJob\\\\src\\\\Services\\\\Profile\\\\Profile.Application\\\\Profiles\\\\Queries\\\\GetAllProfiles\\\\GetAllProfilesQueryHandler.cs:line 24\\r\\n at MediatR.Pipeline.RequestPostProcessorBehavior 2.Handle(TRequest请求,CancellationToken cancelleToken,RequestHandlerDelegate 1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior 2.Handle(TRequest请求,CancellationToken cancelleToken,RequestHandlerDelegate 1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior 2.Handle(TRequ est request, CancellationToken cancellationToken, RequestHandlerDelegate 1 next)\\r\\n at Profile.API.Controllers.ProfilesController.GetAll() in C:\\\\Users\\\\Adam\\\\Repositories\\\\TeamJob\\\\TeamJob\\\\src\\\\Services\\\\Profile\\\\Profile.API\\\\Controllers\\\\ProfilesController.cs:line 19\\r\\n at lambda_method(Closure , Object )\\r\\n at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\\r\\n at System.Threading.Tasks.ValueTask 1.get_Result()\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ControllerAct est请求,CancellationToken cancellingToken,RequestHandlerDelegate 1 next)\\r\\n at Profile.API.Controllers.ProfilesController.GetAll() in C:\\\\Users\\\\Adam\\\\Repositories\\\\TeamJob\\\\TeamJob\\\\src\\\\Services\\\\Profile\\\\Profile.API\\\\Controllers\\\\ProfilesController.cs:line 19\\r\\n at lambda_method(Closure , Object )\\r\\n at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\\r\\n at System.Threading.Tasks.ValueTask 1.get_Result()\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ControllerActionActionInvoker.InvokeActionMethodAsync()\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ControllerActionActionInvoker.InvokeNextActionFilterAsync()\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow (ActionExecutedContext上下文)\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ControllerAct ionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()\\r\\n at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()" } ionInvoker.Next(状态和下一个,作用域和范围,对象和状态,布尔值和已完成)\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ControllerActionAction invoker.InvokeInnerFilterAsync()\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync ()“}

The problem is in Mediator.Send method. 问题出在Mediator.Send方法中。 Mediator class stores request handlers in a static ConcurrentDictionary Mediator类将请求处理程序存储在静态ConcurrentDictionary中

private static readonly ConcurrentDictionary<Type, object> _requestHandlers = new ConcurrentDictionary<Type, object>();

and when the Send method is invoked, it uses GetOrAdd method on that dictionary. 当调用Send方法时,它将在该字典上使用GetOrAdd方法。

var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType, t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));

This means, if the request handler instance doesn't already exist in the dictionary, it creates a new instance (using Activator ) and adds it to the dictionary, but if the request handler instance already exists in the dictionary, it uses the existing one (and this is the cause of your problem). 这意味着,如果请求处理程序实例不存在于字典中,它将创建一个新实例(使用Activator )并将其添加到字典中,但是如果请求处理程序实例已存在于字典中,则它将使用现有的实例(这是造成您问题的原因)。

So, what exactly is causing your error? 那么,到底是什么导致了您的错误?

The _requestHandlers dictionary is static , which means it lives through multiple requests , ie doesn't get disposed/garbage collected at the end of the request. _requestHandlers字典是static ,这意味着它可以通过多个请求生存,即在请求结束时不会被处理/收集。 Your ProfileDbContext , when registered using AddDbContext method, has a scoped lifetime , which means it is created once per request (and disposed at the end of the request). 当您使用AddDbContext方法注册时,您的ProfileDbContext具有作用域范围的有效期 ,这意味着它在每个请求中创建一次 (并在请求结束时处置)。 This means you can end up in a situation where _requestHandlers dictionary contains an instance of GetAllProfilesQueryHandler that has a reference on an exposed instance of ProfileDbContext . 这意味着你可以在一个情况下结束了_requestHandlers字典包含的实例GetAllProfilesQueryHandler是对的暴露实例的引用ProfileDbContext

Here is what happens: 这是发生了什么:

  1. First request arrives. 第一个请求到达。
  2. Mediator.Send(new GetProfileQuery { Id = id }) gets called. Mediator.Send(new GetProfileQuery { Id = id })被调用。
  3. Mediator.Send(new GetProfileQuery { Id = id }) doesn't find GetAllProfilesQueryHandler in _requestHandlers dictionary, so it instantiates it and also resolves its ProfileDbContext dependency. Mediator.Send(new GetProfileQuery { Id = id })_requestHandlers字典中找不到GetAllProfilesQueryHandler ,因此它实例化了它,并且还解析了它的ProfileDbContext依赖项。
  4. The request ends. 请求结束。 _context field ( ProfileDbContext ) in your GetAllProfilesQueryHandler indsance gets disposed (because it has a scoped lifetime), but the _requestHandlers dictionary (containing GetAllProfilesQueryHandler instance) doesn't get disposed (because it is static). _context场( ProfileDbContext )在GetAllProfilesQueryHandler indsance得到处理(因为它有一个scoped寿命),但_requestHandlers字典(含GetAllProfilesQueryHandler实例)没有得到处置(因为它是静态的)。
  5. Another request arrives. 另一个请求到达。
  6. Mediator.Send(new GetProfileQuery { Id = id }) gets called again. Mediator.Send(new GetProfileQuery { Id = id })再次被调用。
  7. This time Mediator.Send(new GetProfileQuery { Id = id }) finds GetAllProfilesQueryHandler instance in _requestHandlers dictionary and uses the existing instance, whose _context field is disposed at the end of the previous request . 这次Mediator.Send(new GetProfileQuery { Id = id })_requestHandlers词典中找到GetAllProfilesQueryHandler实例, 并使用现有实例,该实例的_context字段位于上一个请求的末尾
  8. GetAllProfilesQueryHandler tries to access the disposed _context field, and gets the "Cannot access a disposed object" error. GetAllProfilesQueryHandler尝试访问已处置的_context字段,并获取“无法访问已处置的对象”错误。

Possible solution 可能的解决方案

Don't let Mediator.Send resolve GetAllProfilesQueryHandler s dependencies. 不要让Mediator.Send解析GetAllProfilesQueryHandler的依赖项。

Maybe pass IServiceProvider serviceProvider to your GetAllProfilesQueryHandler and let it resolve its dependencies as needed: 也许将IServiceProvider serviceProvider传递给您的GetAllProfilesQueryHandler并根据需要解决其依赖关系:

public class GetAllProfilesQueryHandler : IRequestHandler<GetAllProfilesQuery, ProfilesListViewModel>
{
    private readonly IServiceProvider _serviceProvider;

    public GetAllProfilesQueryHandler(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task<ProfilesListViewModel> Handle(GetAllProfilesQuery request, CancellationToken cancellationToken)
    {
        return new ProfilesListViewModel
        {
            ProfileDbContext context = (ProfileDbContext)this._serviceProvider.GetService(typeof(ProfileDbContext));
            IMapper mapper = (IMapper)this._serviceProvider.GetService(typeof(IMapper));

            Profiles = await context.Profiles.ProjectTo<ProfileLookupModel>(mapper.ConfigurationProvider).ToListAsync(cancellationToken)
        };
    }
}

Edit: 编辑:

As @Lucian Bargaoanu pointed out in comments, you can resolve handlers through DI, as in https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection 正如@Lucian Bargaoanu在评论中指出的那样,您可以通过DI解决处理程序,如https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 注入 DbContext 时无法访问 ASP.NET Core 中已处理的对象 - Cannot access a disposed object in ASP.NET Core when injecting DbContext 在ASP.NET Core授权属性中将存储库与DbContext一起使用:“无法访问已处置的对象” - Use repository with DbContext in ASP.NET Core Authorize-Attribute: “Cannot access a disposed object” C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET 核心中已处置的 object - C#: Cannot access a disposed object in ASP.NET Core when injecting DbContext with IQueryable .NET Core SocketIO-无法访问已处置的对象 - .NET Core SocketIO - Cannot access disposed object 无法访问ASP.NET Core后台服务中的已处置对象 - Cannot access a disposed object in ASP.NET Core background service 无法访问已处置的 object Asp.net Identity Core - Cannot access a disposed object Asp.net Identity Core 无法访问ABP中Hangfire重复作业的已处置对象(在DbContext上)错误 - Cannot access a disposed object (on DbContext) error on Hangfire recurrent job in ABP SSH.NET“无法访问已处置的对象” - SSH.NET “Cannot access a disposed object” .Net Core 2.1 - 无法访问已处置的 object.Object 名称:'IServiceProvider' - .Net Core 2.1 - Cannot access a disposed object.Object name: 'IServiceProvider' 尝试在 Startup ExceptionHandler 中访问 EF Core DbContext 时“无法访问已释放的上下文” - "Cannot access a disposed context" when trying to access EF Core DbContext in Startup ExceptionHandler
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM