0

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?

platypusjh
  • 667
  • 1
  • 7
  • 16
  • What version of Mediatr are you using? – Alex Dec 31 '17 at 16:58
  • If 3 or above, check out Behaviours https://github.com/jbogard/MediatR/wiki/Behaviors – Alex Dec 31 '17 at 16:59
  • That looks very promising and I believe you've solved my true issue. Thank you!!! If anyone else has a suggestion for my question I'd still love to hear it as the pursuit of the issue has been teaching me a lot about DI registration and reflection and I'd still like to know how to use a decorator across multiple assemblies. – platypusjh Dec 31 '17 at 18:57

0 Answers0