如何装饰MediatR处理程序

时间:2019-02-07 19:21:57

标签: c# autofac cqrs mediatr

我只想装饰一个MediatR处理程序。我尝试使用行为,但“行为”为实现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());

时,如何使下面的类Handle方法首先在ProcessFirstCommandHandler之前调用?
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);
    }
}

2 个答案:

答案 0 :(得分:1)

如果您要做的只是在调用处理程序之前先运行一些东西,那么您可以利用Behaviors来实现这一点。我知道您已经说过您曾经尝试过此操作,但是,您可以创建一个生成行为,该行为运行IRequestPreProcessor的所有实现。

注意:下面的过程可用于在您的处理程序也运行之后实现某些东西,您只需将IRequestPreProcessor的实现更改为IReqiestPostProcessor

因此,如果您有命令处理程序:

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请求上运行,但仅会注入IRequestPreProcessor的实现,该实现使用通用类型或指定TRequest类型,如上面的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的默认Dependency Injector,它将仅注入实现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");
        }
}

AnotherPreProcessFirstCommand.cs

public class AnotherPreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
{

            public ProcessFirstCommandHandlerDecorator()
            {

            }

            public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
            {
                Console.WriteLine("I ran before the handler aswell!");
            }
 }

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

使用前面提到的通用PreProcessBahviour,这将同时注入 GenericPreProcessCommand AnotherGenericPreProcessCommand ,但只能注入 PreProcessFirstCommand AnotherPreProcessFirstCommand < / em>。这似乎只是DI的限制。我在official github issue上为MediatR创作者吉米·博加德(Jimmy Bogard)留下了评论,因此请务必阅读并在其中做出贡献。

祝你好运!

答案 1 :(得分:0)

尽管在大多数情况下推荐使用IRequestPreProcessor作为首选方法,但您仍然可以直接调用IoC容器来添加装饰器。

例如,使用SimpleInjector:

container.RegisterDecorator(typeof(IRequestHandler<,>), typeof(HandlerDecorator<,>));