我的一个微服务网络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; }
}
异常消息
{ “错误”:[ “无法访问已处置的对象。此错误的常见原因是处置从依赖项注入中解析的上下文,然后稍后尝试在应用程序中的其他位置使用相同的上下文实例。如果您在该实例上调用Dispose(),则可能会发生这种情况。上下文,或将上下文包装在using语句中。如果使用依赖项注入,则应让依赖项注入容器负责处理上下文实例。\ r \ n对象名称:'ProfileDbContext'。” ], Microsoft的“ stackTrace”:“ Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()\ r \ n Microsoft的Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()\ r \ n Microsoft的Microsoft.EntityFrameworkCore.DbContext.get_ChangeTracker()\ r \ n。 EntityFrameworkCore.Query.Internal.QueryCompilationContextFactory.get_TrackQueryResults()\ r \ n在Microsoft.EntityFrameworkCore.Query.Internal.QueryCompilationContextFactory.Create(布尔异步)\ r \ n在Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery [TResult](QueryModel在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.IAsyncEnumerable.GetEnumerator()\ r \ n在System.Linq.AsyncEnumerable.Aggregate_ [TSource,TAccumulate,TResult](IAsyncEnumerable1 source, TAccumulate seed, Func
3累加器,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请求,CancellationToken cancelToken,RequestHandlerDelegate1 next)\r\n at MediatR.Pipeline.RequestPreProcessorBehavior
2.Handle(TRequest请求,CancellationToken cancelToken,RequestHandlerDelegate1 next)\r\n at MediatR.Pipeline.RequestPreProcessorBehavior
2.Handle(TRequest请求,CancellationToken cancelToken,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.ControllerActionActionvovo.InvokeActionMethodAsync()在Microsoft .AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()\ r \ n在Microsoft.AspNetCore.Mvc.Internal.ControllerActionActionInvoker.Rethrow(ActionExecutedContext上下文)\ r \ n接下来,Microsoft.AspNetCore.Mvc.Internal.ControllerActionAction.Invoker.InvokeInnerFilterAsync(),Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()中的Scope&范围,Object&state,Boolean&isCompleted)\ r \ n }
答案 0 :(得分:0)
问题出在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
,这意味着它在多个请求中生存,即在请求结束时不会被处理/收集。您的ProfileDbContext
使用AddDbContext
方法注册时,具有scoped lifetime,这意味着它是每个请求创建一次(并放置在请求的结尾) 。这意味着您可能会遇到_requestHandlers
词典包含GetAllProfilesQueryHandler
实例的情况,该实例在ProfileDbContext
的公开实例上具有引用。
发生了什么事
Mediator.Send(new GetProfileQuery { Id = id })
被呼叫。Mediator.Send(new GetProfileQuery { Id = id })
在GetAllProfilesQueryHandler
字典中找不到_requestHandlers
,因此它实例化了它并解决了其ProfileDbContext
的依赖性。_context
中的ProfileDbContext
字段(GetAllProfilesQueryHandler
)被处置(因为它的生存期为scoped
),但是_requestHandlers
字典(包含{{1} }实例)不会被处置(因为它是静态的)。GetAllProfilesQueryHandler
被再次呼叫。Mediator.Send(new GetProfileQuery { Id = id })
在Mediator.Send(new GetProfileQuery { Id = id })
字典中找到GetAllProfilesQueryHandler
个实例,并使用其现有的实例,其_requestHandlers
字段位于上一个请求的结尾< / strong>。_context
尝试访问已处置的GetAllProfilesQueryHandler
字段,并收到“无法访问已处置的对象”错误。可能的解决方案
不要让_context
解决Mediator.Send
的依赖性。
也许可以将GetAllProfilesQueryHandler
传递给您的IServiceProvider serviceProvider
,并根据需要解决它的依赖性:
GetAllProfilesQueryHandler
编辑:
正如@Lucian Bargaoanu在评论中指出的那样,您可以通过DI来解析处理程序,如https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection