Проблема в 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
, имеет срок действия в области , что означает, что создается один раз за запрос (и удаляется в конце запроса). Это означает, что вы можете оказаться в ситуации, когда словарь _requestHandlers
содержит экземпляр GetAllProfilesQueryHandler
, который имеет ссылку на открытый экземпляр ProfileDbContext
.
Вот что происходит:
- Первый запрос поступил.
Mediator.Send(new GetProfileQuery { Id = id })
вызывается.
Mediator.Send(new GetProfileQuery { Id = id })
не находит GetAllProfilesQueryHandler
в словаре _requestHandlers
, поэтому он создает его и также разрешает зависимость ProfileDbContext
.
- Запрос заканчивается. Поле
_context
(ProfileDbContext
) в вашем GetAllProfilesQueryHandler
indsance удаляется (поскольку оно имеет scoped
время жизни), но словарь _requestHandlers
(содержащий экземпляр GetAllProfilesQueryHandler
) не удаляется (потому что оно статический).
- Приходит еще один запрос.
Mediator.Send(new GetProfileQuery { Id = id })
снова вызывается.
- На этот раз
Mediator.Send(new GetProfileQuery { Id = id })
находит GetAllProfilesQueryHandler
экземпляр в _requestHandlers
словаре и использует существующий экземпляр, поле которого _context
расположено в конце предыдущего запроса .
GetAllProfilesQueryHandler
пытается получить доступ к удаленному полю _context
и получает сообщение об ошибке «Не удается получить доступ к удаленному объекту».
Возможное решение
Не позволяйте Mediator.Send
разрешать GetAllProfilesQueryHandler
s зависимости.
Может быть, передать 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:
Как отметил @Lucian Bargaoanu в комментариях, вы можете разрешать обработчики через DI, как в https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection