IMediatR - Generi c Обработчик запросов для generi c Запросы - PullRequest
1 голос
/ 07 февраля 2020

У меня сейчас проблема с 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), чтобы хотя бы сохранить централизованный код.

1 Ответ

0 голосов
/ 08 февраля 2020

MediatR не нашел обработчик запроса для запроса

MediatR - это просто набор интерфейсов; он не отвечает за поиск обработчика запросов - Simple Injector is.

В настоящее время вы используете следующий код для регистрации ваших обработчиков:

container.Register(typeof(IRequestHandler<,>), assemblies);

Это будет регистрировать:

все конкретные, не универсальные c, публичные c и внутренние типы в данном наборе assemblies, которые реализуют данный openGenericServiceType с образом жизни контейнера по умолчанию

(Как указано в XML документации)

. Вы хотите зарегистрировать не только не-generi c, но generi c типы также. Это может быть достигнуто несколькими способами. Например, добавив эти типы c generi:

container.Register(typeof(IRequestHandler<,>), assemblies);
container.Register(typeof(IRequestHandler<,>), typeof(CreateAccessRequestHandler<,>));

Если, однако, у вас есть много реализаций open-generi c, их пакетная регистрация может быть более эффективной:

var types =
    container.GetTypesToRegister(typeof(IRequestHandler<,>), assemblies,
        new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });

container.Register(typeof(IRequestHandler<,>), types);
...