如何在基于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装饰器的注册顺序决定了嵌套层级:先注册的装饰器在外层,后注册的更靠近真实处理程序,刚好匹配你需要的执行顺序。
修改步骤
- 调整命令/查询专属装饰器的实现,改为实现
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
相关产品推荐
相关产品推荐

