【问题标题】:How to decorate a MediatR Handler如何装饰 MediatR 处理程序
【发布时间】: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);
    }
}

【问题讨论】:

    标签: c# autofac cqrs mediatr


    【解决方案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<,>));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-01-24
        • 2011-03-02
        • 2021-06-22
        • 2021-08-24
        • 2020-12-08
        • 2019-08-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多