如何装饰 MediatR 处理程序
Posted
技术标签:
【中文标题】如何装饰 MediatR 处理程序【英文标题】:How to decorate a MediatR Handler 【发布时间】:2019-07-02 00:12:10 【问题描述】:我只想装饰一个 MediatR 处理程序。我尝试使用 Behaviours,但 Behaviors 为每个实现 IRequestHandler<TRequest,TResponse>
的处理程序注入了装饰器
public class ProcessFirstCommand : IRequest<bool>
public string Message get; set;
public class ProcessFirstCommandHandler : IRequestHandler<ProcessFirstCommand, bool>
public Task<bool> Handle(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("Inside Process First Command Handler");
return Task.FromResult(true);
public class Manager
private readonly IMediator _mediator;
public Manager(IMediator mediator)
_mediator = mediator;
public void Execute()
_mediator.Send(new ProcessFirstCommand());
//Registering in Autofac for IRequestHandler
public class Module : Autofac.Module
protected override void Load(ContainerBuilder builder)
builder.RegisterAssemblyTypes(ThisAssembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
问题:如何添加一个装饰器,该装饰器将在调用 ProcessFirstCommandHandler 类的 Handle 方法之前执行,而不是为实现 IRequestHandler 的其他类添加。
当Manager对象执行此行_mediator.Send(new ProcessFirstCommand());
时,如何在ProcessFirstCommandHandler之前首先调用下面的类Handle方法@
public class ProcessFirstCommandHandlerDecorator<TRequest, TResponse> : IRequestHandler<ProcessFirstCommand, bool>
where TRequest : ProcessFirstCommand
private readonly IRequestHandler<ProcessFirstCommand, bool> _handler;
public ProcessFirstCommandHandlerDecorator(IRequestHandler<ProcessFirstCommand, bool> handler)
_handler = handler;
public Task<bool> Handle(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("Inside Process First Command Handler Decorator");
_handler.Handle(request, cancellationToken);
return Task.FromResult(true);
【问题讨论】:
【参考方案1】:如果您想要做的只是在调用处理程序之前让某些东西运行,那么您可以利用行为来实现这一点。我知道你说过你之前已经尝试过,但是,你可以创建一个通用的 Behavior 来运行 IRequestPreProcessor 的所有实现。
注意:以下过程也适用于在处理程序运行后实现某些内容,您只需将接口从 IRequestPreProcessor 更改为 IRequestPostProcessor
所以如果你有你的命令处理程序:
public class ProcessFirstCommandHandler : IRequestHandler<ProcessFirstCommand, bool>
public Task<bool> Handle(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("Inside Process First Command Handler");
return Task.FromResult(true);
你可以实现 IRequestPreProcessor(你需要的装饰器),但一定要指定你希望它运行的命令
public class PreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
public ProcessFirstCommandHandlerDecorator()
public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("Inside Process First Command Handler Decorator");
这将由您的通用 PreProcessorBehaviour 激活,它将在每个 MediatR 请求上运行,但只会注入使用通用类型或指定 TRequest 类型的 IRequestPreProcessor 实现,就像我们上面的 PreProcessFirstCommand 类所做的那样:
public class RequestPreProcessValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
private readonly IEnumerable<IRequestPreProcessor<TRequest>> _preProcessors;
public RequestPreProcessValidationBehaviour(IEnumerable<IRequestPreProcessor<TRequest>> preProcessors)
_preProcessors = preProcessors;
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
foreach (var processor in _preProcessors)
await processor.Process(request, cancellationToken).ConfigureAwait(false);
return await next().ConfigureAwait(false);
注意:此解决方案唯一的小问题是,如果您使用 ASP .NET Core 的默认依赖注入器,它只会注入实现 IRequestPreProcessor 并指定类型的类之一。
例如:
如果你有以下课程:
ProcessFirstCommandHandler.cs
public class ProcessFirstCommandHandler : IRequestHandler<ProcessFirstCommand, bool>
public Task<bool> Handle(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("I'm inside the handler");
return Task.FromResult(true);
PreProcessFirstCommand.cs
public class PreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
public ProcessFirstCommandHandlerDecorator()
public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("I ran before the handler");
另一个PreProcessFirstCommand.cs
public class AnotherPreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
public ProcessFirstCommandHandlerDecorator()
public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("I ran before the handler as well!");
GenericPreProcessCommand.cs
public class GenericPreProcessCommand<TRequest> : IRequestPreprocessor<TRequest>
public ProcessFirstCommandHandlerDecorator()
public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("I'm generic!");
AnotherGenericPreProcessCommand.cs
public class AnotherGenericPreProcessCommand<TRequest> : IRequestPreprocessor<TRequest>
public ProcessFirstCommandHandlerDecorator()
public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
Console.WriteLine("I'm generic aswell!");
使用前面提到的通用 PreProcessorBehavior,这将注入 GenericPreProcessCommand 和 AnotherGenericPreProcessCommand,但只注入 PreProcessFirstCommand 或 AnotherPreProcessFirstCommand。这似乎只是 DI 的限制。我已经在official github issue 上为 MediatR 的创建者 Jimmy Bogard 留下了 cmets,因此请务必在此处阅读并做出贡献。
【讨论】:
【参考方案2】:虽然在大多数情况下使用IRequestPreProcessor
是推荐和首选的方式,但您仍然可以直接调用您的 IoC 容器来添加装饰器。
例如,使用 SimpleInjector:
container.RegisterDecorator(typeof(IRequestHandler<,>), typeof(HandlerDecorator<,>));
【讨论】:
以上是关于如何装饰 MediatR 处理程序的主要内容,如果未能解决你的问题,请参考以下文章
如何在 VSCode 扩展中获取当前装饰列表或处理装饰位置更改?
如何使用 FluentAssertions 在 XUnit 中测试 MediatR 处理程序