I'm able to get my commands and queries working ok with their respective handlers, but nothing from decorators.
I'm using Autofac with MediatR. A complication is that my messages (commands/queries/events) are in a separate project so need to be registered by assembly with Autofac.
Here's my CommandHandler interface:
public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, TResponse>
where TCommand : MediatR.IRequest<TResponse>
{
}
A sample CommandHandler:
public class AddExternalLoginToUserCommandHandler : CommandHandler<AddExternalLoginToUser, CommonResult>
{
private readonly UserManager<AppUser> _userManager;
public AddExternalLoginToUserCommandHandler(
UserManager<AppUser> userManager, ILogger logger) : base(logger)
{
...
}
protected override async Task<CommonResult> HandleImplAsync(
AddExternalLoginToUser command, CancellationToken cancellationToken)
{
... details deleted...
return
new CommonResult(Outcome.Succeeded);
}
}
My Decorator:
public class LoggingCommandDecorator<TCommand, TResult> : ICommandHandler<TCommand, TResult>
where TCommand : ICommand<TResult>
where TResult : ICommonResult
{
private readonly ICommandHandler<TCommand,TResult> _commandHandler;
public LoggingCommandDecorator(ICommandHandler<TCommand,TResult> commandHandler)
{
_commandHandler = commandHandler;
}
public async Task<TResult> Handle(TCommand command, CancellationToken cancellationToken)
{
Debug.WriteLine("***** HOLY $%1@ IT WORKED ******");
TResult result = await _commandHandler.Handle(command, cancellationToken);
return result;
}
}
And my partial configuration:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Database, Authentication and Azure config stuff deleted for clarity...
// AUTOFAC DI/IOC CONFIGURATION
var builder = new ContainerBuilder();
// Copies existing dependencies from IServiceCollection
builder.Populate(services);
// Enables contravariant Resolve() for interfaces with single contravariant ("in") arg
builder
.RegisterSource(new ContravariantRegistrationSource());
// Mediator itself
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
// Request handlers
builder
.Register<SingleInstanceFactory>(ctx => {
var c = ctx.Resolve<IComponentContext>();
return t => { object o; return c.TryResolve(t, out o) ? o : null; };
})
.InstancePerLifetimeScope();
// Notification handlers
builder
.Register<MultiInstanceFactory>(ctx => {
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
// Register query handlers
builder.RegisterAssemblyTypes(typeof(QueryHandler<,>).GetTypeInfo().Assembly).AsImplementedInterfaces(); // via assembly scan
// REGISTER COMMAND HANDLERS AS KEYED SERVICE
Assembly commandHandlerAssembly = typeof(CommandHandler<,>).Assembly;
builder.RegisterAssemblyTypes(commandHandlerAssembly)
.As(t => t.GetInterfaces()
.Where(i => i.IsClosedTypeOf(typeof(ICommandHandler<,>)))
.Select(i => new KeyedService("command-handler", i)))
.AsImplementedInterfaces();
// REGISTER DECORATOR
builder.RegisterGenericDecorator(
typeof(LoggingCommandDecorator<,>),
typeof(ICommandHandler<,>),
fromKey: "command-handler");
// Finalize
var container = builder.Build();
//Create the IServiceProvider based on the container.
return new AutofacServiceProvider(container);
}
Any ideas on getting the decorator handler to fire? As mentioned before commands and queries are working great.
Follow Up: Open Generics and Assembly Scanning
There's discussion on the MediatR Git pages that talks about difficulty with Autofac, Open Generics and Assembly scanning:
https://github.com/jbogard/MediatR/issues/128
It seems Autofac does not support registering open generics when doing assembly scanning...
I'm aware of the issue but it seemed to me it wasn't a factor here. I know the assembly scan and registrations were successful because the command handlers are getting registered properly. Or am I overlooking something?