You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在基于Autofac+MediatR的CQRS项目中为IQueryHandler和ICommandHandler添加自定义装饰器

问题描述

我正在使用Autofac和MediatR库在简单控制台应用中实现CQRS模式,项目结构如下:
项目结构
我希望命令和查询的处理流程如下:

  • 执行Request Handler Decorators前置逻辑
  • 执行Command/Query Handler Decorators前置逻辑
  • 运行Command/Query Handler核心业务逻辑
  • 执行Command/Query Handler Decorators后置逻辑
  • 执行Request Handler Decorators后置逻辑

现有相关代码如下:

主文件

internal class Program
{
    static async Task Main(string[] args)
    {
        var container = ConfigureCQRS();
        using var scope = container.BeginLifetimeScope();

        var mediatr = scope.Resolve<IMediator>();

        await mediatr.Send(new MyCommand("Command1"));
        await mediatr.Send(new MyQuery("Query1"));
    }

    private static IContainer ConfigureCQRS()
    {
        var builder = new ContainerBuilder();

        builder.RegisterModule(new MediatRModule(typeof(Program).Assembly));
        builder.RegisterGenericDecorator(typeof(LoggingRequestHandlerDecorator<,>), typeof(IRequestHandler<,>));
        builder.RegisterGenericDecorator(typeof(UnitOfWorkCommandHandlerDecorator<,>), typeof(ICommandHandler<,>));
        builder.RegisterGenericDecorator(typeof(DiagnosticQueryHandlerDecorator<,>), typeof(IQueryHandler<,>));

        return builder.Build();
    }
}

MediatRModule.cs

internal class MediatRModule : Autofac.Module
{
    private readonly Assembly[] assemblies;

    internal MediatRModule(params Assembly[] assemblies)
    {
        this.assemblies = assemblies;
    }

    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterMediatR(assemblies);

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

        foreach (var openHandlerType in openHandlerTypes)
        {
            builder.RegisterAssemblyTypes(this.assemblies)
             .AsClosedTypesOf(openHandlerType)
             .AsImplementedInterfaces();
        }

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

        builder.Register<ServiceFactory>(outerContext =>
        {
            var innerContext = outerContext.Resolve<IComponentContext>();

            return serviceType => innerContext.Resolve(serviceType);
        })
            .InstancePerLifetimeScope();
    }
}

典型命令实现

internal record MyCommand(string Text) : CommandBase<string>;

internal class MyCommandHandler : ICommandHandler<MyCommand, string>
{
    public Task<string> Handle(MyCommand command, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Command result: {command.Text}");
        return Task.FromResult(command.Text);
    }
}

通用请求处理装饰器实现

internal class LoggingRequestHandlerDecorator<TRequest, TResult> :
    IRequestHandler<TRequest, TResult>
    where TRequest : IRequest<TResult>
{
    private readonly IRequestHandler<TRequest, TResult> _decorated;

    public LoggingRequestHandlerDecorator(IRequestHandler<TRequest, TResult> decorated)
    {
        this._decorated = decorated;
    }

    public async Task<TResult> Handle(TRequest request, CancellationToken cancellationToken)
    {
        Console.WriteLine("LoggingRequestHandlerDecorator start");
        var result = await _decorated.Handle(request, cancellationToken);
        Console.WriteLine("LoggingRequestHandlerDecorator end");
        return result;
    }
}

命令处理装饰器实现

internal class UnitOfWorkCommandHandlerDecorator<TCommand, TResult> :
    ICommandHandler<TCommand, TResult>
    where TCommand : ICommand<TResult>
{
    private readonly ICommandHandler<TCommand, TResult> _decorated;

    public UnitOfWorkCommandHandlerDecorator(ICommandHandler<TCommand, TResult> decorated)
    {
        _decorated = decorated;
    }

    public async Task<TResult> Handle(TCommand command, CancellationToken cancellationToken)
    {
        Console.WriteLine("UnitOfWorkCommandHandlerDecorator start");
        var result = await _decorated.Handle(command, cancellationToken);
        Console.WriteLine("UnitOfWorkCommandHandlerDecorator end");
        return result;
    }
}

解决方案

核心逻辑说明

MediatR内部仅依赖IRequestHandler<,>接口解析请求处理程序,原本直接为自定义子接口ICommandHandler<,>、IQueryHandler<,>注册的装饰器不会被MediatR触发。Autofac装饰器的注册顺序决定了嵌套层级:先注册的装饰器在外层,后注册的更靠近真实处理程序,刚好匹配你需要的执行顺序。

修改步骤

  1. 调整命令/查询专属装饰器的实现,改为实现IRequestHandler<,>接口,构造函数注入IRequestHandler<,>类型的被装饰对象,保留原有泛型约束:
internal class UnitOfWorkCommandHandlerDecorator<TRequest, TResult> :
    IRequestHandler<TRequest, TResult>
    where TRequest : ICommand<TResult>
{
    private readonly IRequestHandler<TRequest, TResult> _decorated;

    public UnitOfWorkCommandHandlerDecorator(IRequestHandler<TRequest, TResult> decorated)
    {
        _decorated = decorated;
    }

    public async Task<TResult> Handle(TRequest request, CancellationToken cancellationToken)
    {
        Console.WriteLine("UnitOfWorkCommandHandlerDecorator start");
        var result = await _decorated.Handle(request, cancellationToken);
        Console.WriteLine("UnitOfWorkCommandHandlerDecorator end");
        return result;
    }
}

按相同逻辑修改查询专属的DiagnosticQueryHandlerDecorator<,>实现即可。
2. 调整装饰器注册逻辑,将命令/查询专属装饰器也注册到IRequestHandler<,>接口上,添加条件过滤仅作用于对应类型的请求,同时保证注册顺序符合嵌套要求:

private static IContainer ConfigureCQRS()
{
    var builder = new ContainerBuilder();

    builder.RegisterModule(new MediatRModule(typeof(Program).Assembly));

    // 外层通用Request装饰器:最先执行前置,最后执行后置
    builder.RegisterGenericDecorator(typeof(LoggingRequestHandlerDecorator<,>), typeof(IRequestHandler<,>));

    // 内层命令专属装饰器:仅装饰实现了ICommand<>的请求
    builder.RegisterGenericDecorator(typeof(UnitOfWorkCommandHandlerDecorator<,>), typeof(IRequestHandler<,>))
        .When(ctx => ctx.GenericTypeArguments[0].GetInterfaces().Any(i => 
            i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommand<>)));

    // 内层查询专属装饰器:仅装饰实现了IQuery<>的请求
    builder.RegisterGenericDecorator(typeof(DiagnosticQueryHandlerDecorator<,>), typeof(IRequestHandler<,>))
        .When(ctx => ctx.GenericTypeArguments[0].GetInterfaces().Any(i => 
            i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQuery<>)));

    return builder.Build();
}

执行效果

修改后命令的执行顺序完全符合需求:

LoggingRequestHandlerDecorator start
UnitOfWorkCommandHandlerDecorator start
Command result: Command1
UnitOfWorkCommandHandlerDecorator end
LoggingRequestHandlerDecorator end

查询的执行逻辑同理,会触发通用请求装饰器和查询专属装饰器的对应逻辑。


内容的提问来源于stack exchange,提问作者Vladislav Horbachov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 02:06:08