![](/img/trans.png)
[英]Cannot access a disposed object in ASP.NET Core when injecting DbContext
[英]Cannot access a disposed object for DbContext in .NET Core for Async method
我的一個微服務網絡api出現了一個奇怪的問題。 我的異步GET方法為我的DbContext拋出“無法訪問已處置的對象”異常,除非是第一次調用它們。 我嘗試在網上尋找答案,但沒有任何效果。 我確保我的方法不會異步無效,並且等待必要的調用。 由於我的POST和DELETE方法工作正常,因此我可以確定真正的罪魁禍首是IMapper實例。 我認為它可能總是指向DbContext的第一個實例,這就是為什么該工程第一次生效而后一次沒有生效的原因。 任何幫助或指針將不勝感激
這是代碼的一些快照。
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
// 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
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
[MongoDatabase("profileDb")]
public class ProfileDbContext : DbContext
{
public ProfileDbContext(DbContextOptions<ProfileDbContext> options)
: base(options)
{
}
public DbSet<Domain.Entities.Profile> Profiles { get; set; }
}
異常消息
{“ error”:[“無法訪問已處置的對象。此錯誤的常見原因是處置從依賴項注入中解決的上下文,然后稍后嘗試在應用程序中的其他位置使用相同的上下文實例。在對象上調用Dispose()或將上下文包裝在using語句中。如果使用依賴項注入,則應讓依賴項注入容器來處理上下文實例。\\ r \\ n對象名稱:'ProfileDbContext'。” ],“ 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在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請求,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(狀態和下一個,作用域和范圍,對象和狀態,布爾值和已完成)\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ControllerActionAction invoker.InvokeInnerFilterAsync()\\ r \\ n在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync ()“}
問題出在Mediator.Send方法中。 Mediator
類將請求處理程序存儲在靜態ConcurrentDictionary中
private static readonly ConcurrentDictionary<Type, object> _requestHandlers = new ConcurrentDictionary<Type, object>();
當調用Send
方法時,它將在該字典上使用GetOrAdd
方法。
var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType, t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));
這意味着,如果請求處理程序實例不存在於字典中,它將創建一個新實例(使用Activator
)並將其添加到字典中,但是如果請求處理程序實例已存在於字典中,則它將使用現有的實例(這是造成您問題的原因)。
那么,到底是什么導致了您的錯誤?
_requestHandlers
字典是static
,這意味着它可以通過多個請求生存,即在請求結束時不會被處理/收集。 當您使用AddDbContext
方法注冊時,您的ProfileDbContext
具有作用域范圍的有效期 ,這意味着它在每個請求中創建一次 (並在請求結束時處置)。 這意味着你可以在一個情況下結束了_requestHandlers
字典包含的實例GetAllProfilesQueryHandler
是對的暴露實例的引用ProfileDbContext
。
這是發生了什么:
Mediator.Send(new GetProfileQuery { Id = id })
被調用。 Mediator.Send(new GetProfileQuery { Id = id })
在_requestHandlers
字典中找不到GetAllProfilesQueryHandler
,因此它實例化了它,並且還解析了它的ProfileDbContext
依賴項。 _context
場( ProfileDbContext
)在GetAllProfilesQueryHandler
indsance得到處理(因為它有一個scoped
壽命),但_requestHandlers
字典(含GetAllProfilesQueryHandler
實例)沒有得到處置(因為它是靜態的)。 Mediator.Send(new GetProfileQuery { Id = id })
再次被調用。 Mediator.Send(new GetProfileQuery { Id = id })
在_requestHandlers
詞典中找到GetAllProfilesQueryHandler
實例, 並使用現有實例,該實例的_context
字段位於上一個請求的末尾 。 GetAllProfilesQueryHandler
嘗試訪問已處置的_context
字段,並獲取“無法訪問已處置的對象”錯誤。 可能的解決方案
不要讓Mediator.Send
解析GetAllProfilesQueryHandler
的依賴項。
也許將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)
};
}
}
編輯:
正如@Lucian Bargaoanu在評論中指出的那樣,您可以通過DI解決處理程序,如https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.