仅针对特定接口类型(命令)执行 MediatR 预处理器

pla*_*sjh 5 c# generics autofac cqrs mediatr

[注意:这是一个“替换”问题。第一个是基于我的主项目的代码,因此我使用来自单一用途项目的代码重新提出了这个问题,该项目更清楚地说明了原理。问题仍然相同,只是表述得更好。]

场景

我正在尝试使用 MediatR 管道行为和 Autofac 进行请求路由,在 CQRS 请求管道上设置命令预处理器。我的目标是预处理器仅针对命令 (ICommand<>) 运行,而不是针对所有请求 (IRequest<>) 运行,这将导致预处理器针对命令、查询和事件执行。

问题

我可以让我的 GenericPreProcessor 或任何其他预处理器对所有类型的请求都能正常运行,但是我用来尝试“过滤”注入的任何方法要么返回错误,要么根本不执行所需的预处理器。

我在 Autofac 中处理所有请求的管道配置如下所示:

// Pipeline pre/post processors
builder
    .RegisterGeneric(typeof(RequestPostProcessorBehavior<,>))
    .As(typeof(IPipelineBehavior<,>));

builder
    .RegisterGeneric(typeof(RequestPreProcessorBehavior<,>))
    .As(typeof(IPipelineBehavior<,>));

// Works as desired: Fires generic pre-processor for ALL requests, both cmd and query
builder
    .RegisterGeneric(typeof(GenericRequestPreProcessor<>))
    .As(typeof(IRequestPreProcessor<>));

// Works for all requests, but I need a way to limit it to commands
builder
    .RegisterGeneric(typeof(MyCommandPreProcessor<>))
    .As(typeof(IRequestPreProcessor<>));
Run Code Online (Sandbox Code Playgroud)

从概念上讲,我正在尝试做类似的事情,但失败了:

builder
    .RegisterGeneric(typeof(MyCommandPreProcessor<>)) // Note generic
    .As(typeof(IRequestPreProcessor<ICommand<>>));
    // Intellisense error "Unexpected use of an unbound generic"

builder
    .RegisterType(typeof(MyCommandPreProcessor)) // Note non-generic
    .As(typeof(IRequestPreProcessor<ICommand<>>)); 
    // Intellisense error "Unexpected use of an unbound generic"

builder
    .RegisterType(typeof(MyCommandPreProcessor)) // Note non-generic
    .As(typeof(IRequestPreProcessor<ICommand<CommonResult>>)); 
    // No errors, but MyCommandPreProcessor not firing
Run Code Online (Sandbox Code Playgroud)

我正在为 MyCommandPreProcessor 尝试几种不同的配置,一个通用的和一个非通用的,但我被以下任一配置所困扰:

public class MyCommandPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
    public Task Process(TRequest request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
        return Task.CompletedTask;
    }
}

- OR -

public class MyCommandPreProcessor : IRequestPreProcessor<IRequest<ICommonResponse>>
{
    public Task Process(TRequest request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题

关于如何注册一个预处理器,该预处理器将被限制为仅触发 ICommand<> 封闭类型的 IRequest<> 类型,有什么想法吗?

辅助材料

GitHub 上的项目

整个最小示例项目可以在https://github.com/jhoiby/MediatRPreProcessorTest查看或克隆

Autofac MediatR 配置

一个工作配置,带有一个用于所有请求的 GenericRequestPreProcessor。

        builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();

        var mediatrOpenTypes = new[]
        {
            typeof(IRequestHandler<,>),
            typeof(IRequestHandler<>),
            typeof(INotificationHandler<>)
        };

        foreach (var mediatrOpenType in mediatrOpenTypes)
        {
            // Register all command handler in the same assembly as WriteLogMessageCommandHandler
            builder
                .RegisterAssemblyTypes(typeof(MyCommandHandler).GetTypeInfo().Assembly)
                .AsClosedTypesOf(mediatrOpenType)
                .AsImplementedInterfaces();

            // Register all QueryHandlers in the same assembly as GetExternalLoginQueryHandler
            builder
                .RegisterAssemblyTypes(typeof(MyQueryHandler).GetTypeInfo().Assembly)
                .AsClosedTypesOf(mediatrOpenType)
                .AsImplementedInterfaces();
        }

        // Pipeline pre/post processors
        builder.RegisterGeneric(typeof(RequestPostProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
        builder.RegisterGeneric(typeof(RequestPreProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
        builder.RegisterGeneric(typeof(GenericRequestPreProcessor<>)).As(typeof(IRequestPreProcessor<>));
        // builder.RegisterGeneric(typeof(GenericRequestPostProcessor<,>)).As(typeof(IRequestPostProcessor<,>));
        // builder.RegisterGeneric(typeof(GenericPipelineBehavior<,>)).As(typeof(IPipelineBehavior<,>));

        builder.Register<SingleInstanceFactory>(ctx =>
        {
            var c = ctx.Resolve<IComponentContext>();
            return t => c.Resolve(t);
        });

        builder.Register<MultiInstanceFactory>(ctx =>
        {
            var c = ctx.Resolve<IComponentContext>();
            return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
        });
Run Code Online (Sandbox Code Playgroud)

MyCommandPreProcessor 类

我正在尝试通用和非通用的这两种方法:

public class MyCommandPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
    public Task Process(TRequest request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
        return Task.CompletedTask;
    }
}

- AND -

public class MyCommandPreProcessor : IRequestPreProcessor<IRequest<ICommonResponse>>
{
    public Task Process(TRequest request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

继承结构

// Requests

IMediatR.IRequest<TResponse>
    <- IMessage<TResponse>
        <- ICommand<TResponse>
            <- concrete MyCommand : ICommand<CommonResponse>
        <- IQuery<TResponse>
            <- concrete MyQuery : IQuery<CommonResponse>

// Request Handlers

IMediatR.IRequestHandler<in TRequest,TResponse>
    <- IMessageHandler<in TRequest,TResponse>
        <- ICommandHandler<in TRequest,TResponse> 
            <- concrete MyCommandHandler : ICommandHandler<MyCommand,CommonResponse>
        <- IQueryHandler<In TRequest,TResponse>
            <- concrete MyQueryHandler : IQueryHandler<MyQuery,CommonResponse>

// CommonResponse - A POCO that returns result info

ICommonResponse
    <- concrete CommonResponse
Run Code Online (Sandbox Code Playgroud)

命令

public interface IMessage<TResponse> : MediatR.IRequest<TResponse>
{
}

public interface ICommand<TResponse> : IMessage<TResponse>
{
}

public class MyCommand : ICommand<CommonResponse>
{
}
Run Code Online (Sandbox Code Playgroud)

命令处理程序

public interface IMessageHandler<in TRequest, TResponse> 
    : MediatR.IRequestHandler<TRequest, TResponse> 
        where TRequest : IRequest<TResponse>
{
}

public interface ICommandHandler<in TRequest, TResponse> 
    : IMessageHandler<TRequest, TResponse> 
        where TRequest : IRequest<TResponse>
{
}

public class MyCommandHandler : ICommandHandler<MyCommand, CommonResponse>
{
    public async Task<CommonResponse> Handle(
        MyCommand request, 
        CancellationToken cancellationToken)
    {
        Debug.WriteLine("   ***** Command handler executing *****");

        return
            new CommonResponse(
                succeeded: true,
                data: "Command execution completed successfully.");
    }
}
Run Code Online (Sandbox Code Playgroud)

预处理器注入目标(在 MediatR 管道代码中)

接收注入的 IRequestPreProcessor<> 的构造函数是:

public RequestPreProcessorBehavior(IEnumerable<IRequestPreProcessor<TRequest>> preProcessors)
    {
        ...
    }
Run Code Online (Sandbox Code Playgroud)

可以在 Github 上的文件第 17 行上看到:

https://github.com/jbogard/MediatR/blob/master/src/MediatR/Pipeline/RequestPreProcessorBehavior.cs

谢谢你!

rda*_*ins 0

我和你有同样的情况,我相信问题是由于RequestPreProcessorBehavior<TRequest, TResponse>没有将所有类型传递给IRequestPreProcessor<TRequest>.

您可以:

  1. 无限制:检查everyrequestMyCommandPreProcessor<TRequest>的类型: IRequestPreProcessor
public Task Process(TRequest request, CancellationToken cancellationToken)
{
    var isCommand = typeof(TRequest).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommand<>));
    if (isCommand)
    {
        // Magic
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 创建您自己的预处理行为,TRequest公开IPipelineBehavior<TRequest, TResponse>
public interface IRequestPreProcessor<in TRequest, TResponse> : IRequestPreProcessor<TRequest>
    where TRequest : IRequest<TResponse>
{
}


public class MyRequestPreProcessorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IRequestPreProcessor<TRequest, TResponse>> _preProcessors;

    public RequestPreProcessorBehavior(IEnumerable<IRequestPreProcessor<TRequest, TResponse>> 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)

IRequestPreProcessor<TRequest, TResponse>使用选项 2,您可以向为命令/查询特定预处理器实现的任何类添加约束。