How can I add custom decorators for IQueryHandler
and ICommandHandler
?
I'm trying to implement CQRS pattern in simple console app, using Autofac and MediatR library.
The project has the next structure:
I want to make processing of commands and queries in this way:
Main File:
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();
}
}
Typical command with decorator:
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);
}
}
Typical request handler decorator:
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;
}
}
Typical command handler decorator:
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;
}
}
Full project published on the github: github.com/LeftTwixWand/CommandQueryDecorators
Ok, I've sole this. Just need to accept IRequestHandler in the constructor of decorators.
Also, I created an example on GitHub of how you can do it: https://github.com/LeftTwixWand/ModernCQRS
This repository demonstrates: