IMediatR - 通用請求的通用請求處理程序
我目前面臨 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);