Dot-Net

IMediatR - 通用請求的通用請求處理程序

  • February 9, 2020

我目前面臨 IMediatR IRequestHandlers 和 IRequest 的問題。為了提供一點上下文,在我們的工具上,使用者可以通過填寫幾個欄位來請求訪問該工具。目前,我們有兩個訪問級別,這意味著我們應該有兩個 IRequestHandler,每個一個。由於我們要做的只是將訪問請求註冊到數據庫並發送電子郵件,因此我認為可以創建一個通用的 IRequestHandler 來處理這些情況(如果我們創建移動訪問級別,它將在未來有所幫助)。

我對如何通用注入這些類型的處理程序進行了一些研究,但我只能找到非通用注入。我想知道是否有辦法為我在下面展示的那些 IRequests 注入這些類型的通用 IRequestHandlers?

這是我為 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正確泛型類型的類,然後它就起作用了:

public sealed class CreateApplicationAccessRequestHandler : CreateAccessRequestHandler<CreateApplicationAccessRequestViewModel, ApplicationAccessRequestNotificationModel, ApplicationAccessRequest>
   {
       public CreateApplicationAccessRequestHandler(IRepository<ApplicationAccessRequest> accessRequestRepository, IMediator mediator, IMapper mapper)
           : base(accessRequestRepository, mediator, mapper)
       { }
   }

我的想法是只使用通用處理程序,而不需要為每個訪問請求類型創建派生類。有沒有辦法做到這一點?

編輯:

我使用了史蒂文在他的答案中放置的程式碼,它幾乎適用於我的情況。我將在下面解釋這一點

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);
}

我分別註冊了泛型和非泛型類型,它在大多數情況下都有效,除非你有一個不可解析的類型,這是我使用TEntity的情況。

SimpleInjector 不知道如何解析 TEntity,因為(我猜)我沒有在定義中將它提供給 IRequestHandler。

如果我在請求中稍微放置了 TEntity(即CreateAccessRequest<TViewModel, TEntity>),它會起作用,但它會破壞項目的架構。所以我選擇了我在這裡提到的備份實現(從那些通用的創建具體的處理程序)至少保持程式碼集中。

MediatR 沒有找到請求的請求處理程序

MediatR 只是一堆介面;它不負責查找請求處理程序——Simple Injector 負責。

您目前正在使用以下程式碼來註冊您的處理程序:

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

這將註冊:

給定集合中的所有具體、非泛型、公共和內部類型,它們使用容器的預設生活方式assemblies實現給定openGenericServiceType

(如 XML 文件所述)

您要做的不僅是註冊非泛型,還要註冊泛型類型。這可以通過多種方式實現。例如通過附加這些泛型類型:

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

但是,如果您有許多開放通用的實現,那麼批量註冊它們可能會更有效:

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

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

引用自:https://stackoverflow.com/questions/60118890