如何装饰 MediatR 处理程序

Rup*_*esh 6 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<,>));
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:如何添加一个装饰器,该装饰器将在调用 ProcessFirstCommandHandler 类的 Handle 方法之前执行,而不是在实现 IRequestHandler 的其他类中执行。

当 Manager 对象执行此行时,如何在 ProcessFirstCommandHandler 之前首先调用下面的类 Handle 方法_mediator.Send(new ProcessFirstCommand());

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 3

如果您想做的只是在调用处理程序之前运行一些东西,那么您可以利用行为来实现这一点。我知道您说过您以前尝试过此操作,但是,您可以创建一个运行 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以实现 IRequestPreProcessor (您所需的装饰器),但请务必指定您希望其运行的命令

public class PreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
{
    public ProcessFirstCommandHandlerDecorator()
    {
        
    }

    public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("Inside Process First Command Handler Decorator");
    }
}
Run Code Online (Sandbox Code Playgroud)

这将由通用 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:此解决方案唯一的小问题是,如果您使用 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

PreProcessFirstCommand.cs

public class PreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
{    
    public ProcessFirstCommandHandlerDecorator()
    {

    }

    public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("I ran before the handler");
    }
}
Run Code Online (Sandbox Code Playgroud)

AnotherPreProcessFirstCommand.cs

public class AnotherPreProcessFirstCommand : IRequestPreprocessor<ProcessFirstCommand>
{
    public ProcessFirstCommandHandlerDecorator()
    {

    }

    public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("I ran before the handler as well!");
    }
 }
Run Code Online (Sandbox Code Playgroud)

通用PreProcessCommand.cs

public class GenericPreProcessCommand<TRequest> : IRequestPreprocessor<TRequest>
{
    public ProcessFirstCommandHandlerDecorator()
    {

    }

    public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("I'm generic!");
    }
 }
Run Code Online (Sandbox Code Playgroud)

AnotherGenericPreProcessCommand.cs

public class AnotherGenericPreProcessCommand<TRequest> : IRequestPreprocessor<TRequest>
{
    public ProcessFirstCommandHandlerDecorator()
    {

    }

    public Task Process(ProcessFirstCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("I'm generic aswell!");
    }
 }
Run Code Online (Sandbox Code Playgroud)

使用前面提到的通用 PreProcessorBehavior,这将注入GenericPreProcessCommandAnotherGenericPreProcessCommand ,但仅注入PreProcessFirstCommandAnotherPreProcessFirstCommand之一。这似乎只是 DI 的一个限制。我已经在官方 github 问题上为 MediatR 创建者 Jimmy Bogard 留下了评论,所以请务必阅读并在那里做出贡献。