簡體   English   中英

無法在.NET Core for Async方法中訪問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](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請求,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(狀態和下一個,作用域和范圍,對象和狀態,布爾值和已完成)\\ 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

這是發生了什么:

  1. 第一個請求到達。
  2. Mediator.Send(new GetProfileQuery { Id = id })被調用。
  3. Mediator.Send(new GetProfileQuery { Id = id })_requestHandlers字典中找不到GetAllProfilesQueryHandler ,因此它實例化了它,並且還解析了它的ProfileDbContext依賴項。
  4. 請求結束。 _context場( ProfileDbContext )在GetAllProfilesQueryHandler indsance得到處理(因為它有一個scoped壽命),但_requestHandlers字典(含GetAllProfilesQueryHandler實例)沒有得到處置(因為它是靜態的)。
  5. 另一個請求到達。
  6. Mediator.Send(new GetProfileQuery { Id = id })再次被調用。
  7. 這次Mediator.Send(new GetProfileQuery { Id = id })_requestHandlers詞典中找到GetAllProfilesQueryHandler實例, 並使用現有實例,該實例的_context字段位於上一個請求的末尾
  8. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM