[英]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_01.<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](IAsyncEnumerable1 source, TAccumulate seed, Func
3 accumulator, Func2 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, RequestHandlerDelegate1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior
2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior
2.Handle(TRequ yncEnumerable.GetEnumerator(个)\\ r \\ n在System.Linq.AsyncEnumerable.Aggregate_ [TSource,TAccumulate,TResult](IAsyncEnumerable1 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,RequestHandlerDelegate1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior
2.Handle(TRequest请求,CancellationToken cancelleToken,RequestHandlerDelegate1 next)\\r\\n at MediatR.Pipeline.RequestPreProcessorBehavior
2.Handle(TRequ est request, CancellationToken cancellationToken, RequestHandlerDelegate1 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,RequestHandlerDelegate1 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: 这是发生了什么:
Mediator.Send(new GetProfileQuery { Id = id })
gets called. Mediator.Send(new GetProfileQuery { Id = id })
被调用。 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
依赖项。 _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
实例)没有得到处置(因为它是静态的)。 Mediator.Send(new GetProfileQuery { Id = id })
gets called again. Mediator.Send(new GetProfileQuery { Id = id })
再次被调用。 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
字段位于上一个请求的末尾 。 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.