繁体   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