Autofac resolve dependency in CQRS CommandDispatcher

你。 提交于 2019-11-30 08:43:19

With Autofac, you need to inject the IComponentContext into the dispatcher. This way you can call back into the container to resolve the required command handler:

public class AutofacCommandDispatcher : ICommandDispatcher
{
    private readonly IComponentContext context;

    public AutofacCommandDispatcher(IComponentContext context)
    {
        this.context = context;
    }

    public void Dispatch<TCommand>(TCommand command)
    {
        var handler = this.context.Resolve<ICommandHandler<TCommand>>();

        void handler.Execute(command);
    }
}

You can register the AutofacCommandDispatcher as follows:

builder.RegisterType<AutofacCommandDispatcher>().As<ICommandDispatcher>();

And you can register all your command handlers in one go as follows:

builder.RegisterAssemblyTypes(myAssembly)
    .AsClosedTypesOf(typeof(ICommandHandler<>));

Two notes though. First of all, you probably defined the ICommandHandler<T> as contravariant (with the in keyword) because Resharper said so, but this is a bad idea for command handlers. There is always a one-to-one mapping between a command and command handler, but defining the in keyword, communicates that there can be multiple implementations.

Second, in my opinion, having a command dispatcher is a bad idea, because this can hide the fact that the consuming classes of command handlers have too many dependencies, which is an indication of a violation of the Single Responsibility Principle. Further more, using such a dispatcher postpones creation of part of the object graph (the part of the command handler) until the command is actually executed (opposed to when the consumer is resolved). This makes it harder to verify the container's configuration. When command handlers are injected directly, you know for sure that the whole object graph can be resolved when the root types in your configuration can be resolved. It's easy to define a command but forget to create the corresponding command handler, so you will need to add unit tests for this to check if each command has a corresponding handler. You can save yourself from having to write such test if you remove the dispatcher all together.

Assuming you have ConcreteCommand : IComman and ConcreteCommandHandler : ICommandHandler<ConcreteCommand> use RegisterType method like this:

builder.RegisterType<ConcreteCommandHandler>()
       .As<ICommandHandler<ConcreteCommand>>();

And then inject your handler:

private ICommandHandler<ConcreteCommand> concreteCommandHandler;

Also look at the automatic assembly types registration Autofac abilities.

If you'd like to resolve ICommandHandler by ICommand implementation then factory registration can help. Register Func<Type, ICommandHandler> or define special class that will resolve appropriate command handler.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!