У меня сейчас проблема с IMediatR IRequestHandlers и IRequest. Чтобы дать немного контекста, в нашем инструменте пользователь может запросить доступ к инструменту, заполнив пару полей. В настоящее время у нас есть два уровня доступа, то есть у нас должно быть два IRequestHandlers, по одному для каждого. Поскольку нам нужно просто зарегистрировать запрос на доступ к базе данных и отправить электронное письмо, я подумал, что можно создать обобщенный c IRequestHandler для обработки таких ситуаций (и это поможет в будущем, если мы создадим уровни перемещения access).
Я провел небольшое исследование о том, как в общем случае вводить эти типы обработчиков, но я мог найти только не-generi c инъекции. Интересно, есть ли способ внедрить эти типы generi c IRequestHandlers для тех IRequests, которые я представляю прямо ниже?
Вот код, который я создал для IRequest.
public class CreateAccessRequest<TViewModel> : IRequest<OperationResult<long>>, IValidatable
where TViewModel : AccessRequestViewModel
{
public TViewModel Request { get; set; }
}
Вот обработчик запросов, более обобщенный, который я мог бы создать.
public class CreateAccessRequestHandler<TViewModel, TNotificationModel, TEntity> : IRequestHandler<CreateAccessRequest<TViewModel>, OperationResult<long>>
where TViewModel : AccessRequestViewModel
where TEntity : Entity
where TNotificationModel : INotification
{
private readonly IRepository<TEntity> _accessRequestRepository;
private readonly IMediator _mediator;
private readonly IMapper _mapper;
public CreateAccessRequestHandler(IRepository<TEntity> accessRequestRepository, IMediator mediator, IMapper mapper)
{
// initalization ...
}
public async Task<OperationResult<long>> Handle(CreateAccessRequest<TViewModel> request, CancellationToken cancellationToken)
{
// mapping, saving to database and sending notifications...
}
}
И, наконец, вот код инъекции для обработчиков
void ConfigureMediatR(Container container)
{
var assemblies = GetAssemblies().ToArray();
container.RegisterSingleton<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), assemblies);
}
Я создаю запрос так:
// CreateApplicationAccessRequestViewModel inherits AccessRequestViewModel
var request = new CreateAccessRequest<CreateApplicationAccessRequestViewModel>();
MediatR не сделал нашел обработчик запроса для запроса и сгенерировал это исключение:
Error constructing handler for the request of type MediatR.IRequestHandler<CreateAccessRequest<CreateApplicationAccessRequestViewModel>, OperationResult<long>>. Register your handlers with the container. See the samples in GitHub for example.
Я не мог сойти с рук только с этой реализацией, мне пришлось создать класс, который наследуется от CreateAccessRequestHandler
с правильным generi c типов, а затем все заработало:
public sealed class CreateApplicationAccessRequestHandler : CreateAccessRequestHandler<CreateApplicationAccessRequestViewModel, ApplicationAccessRequestNotificationModel, ApplicationAccessRequest>
{
public CreateApplicationAccessRequestHandler(IRepository<ApplicationAccessRequest> accessRequestRepository, IMediator mediator, IMapper mapper)
: base(accessRequestRepository, mediator, mapper)
{ }
}
Моя идея состоит в том, чтобы использовать только обработчик generi c без необходимости создавать производные классы для каждого из типов запросов доступа. Есть ли способ сделать это?
--- РЕДАКТИРОВАТЬ: ---
Я использовал код, который Стивен поместил в своем ответе, и он почти работал в моем дело. Я объясню это почти ниже:
var types = container.GetTypesToRegister(typeof(IRequestHandler<,>), assemblies,
new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });
container.Register(typeof(IRequestHandler<,>), types.Where(t => !t.IsGenericTypeDefinition));
foreach(var openGenericType in types.Where(t => t.IsGenericTypeDefinition)) {
container.Register(typeof(IRequestHandler<,>), openGenericType);
}
Я зарегистрировал типы generi c и non-generi c отдельно, и это работает в большинстве ситуаций, , за исключением когда у вас неразрешимый тип, как в моем случае с TEntity .
SimpleInjector не знает, как разрешить TEntity, потому что (я полагаю) я не предоставлял его IRequestHandler в определении.
Это сработало бы, если бы я немного поместил TEntity в запрос (т.е. CreateAccessRequest<TViewModel, TEntity>
), но это нарушило бы архитектуру проекта. Поэтому я выбираю реализацию резервного копирования, о которой я упоминал здесь (создайте конкретные обработчики из этих обобщенных c), чтобы хотя бы сохранить централизованный код.