Autofac解决CQRS CommandDispatcher中的依赖关系

时间:2014-10-27 20:00:02

标签: c# .net dependency-injection autofac cqrs

我正在尝试实现一个简单的CQRS应用程序示例。

这是我的“命令”部分的结构:

public interface ICommand
{
}

//base interface for command handlers
interface ICommandHandler<in TCommand> where TCommand: ICommand
{
    void Execute(TCommand command);
}

// example of the command
public class SimpleCommand: ICommand 
{
   //some properties
}

// example of the SimpleCommand command handler
public class SimpleCommandHandler: ICommandHandler<SimpleCommand>
{
    public void Execute(SimpleCommand command)
    {
       //some logic
    }

}

这是界面ICommandDipatcher。它将命令分派给其处理程序。

public interface ICommandDispatcher
{
    void Dispatch<TCommand>(TCommand command) where TCommand : ICommand;
}

这是ICommandDispatcher的默认实现,主要问题是通过Autofac命令的类型获取必要的命令处理程序。

public class DefaultCommandDispatcher : ICommandDispatcher
{
    public void Dispatch<TCommand>(TCommand command) where TCommand : ICommand
    {
        //How to resolve/get object of the neseccary command handler 
        //by the type of command (TCommand)
        handler.Execute(command);
    }
}

在这种情况下,通过 Autofac 按命令类型解决ICommandHanler的实施的最佳方法是什么?

谢谢!

2 个答案:

答案 0 :(得分:12)

使用Autofac,您需要将IComponentContext注入调度程序。这样,您可以回调容器来解析所需的命令处理程序:

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);
    }
}

您可以按如下方式注册AutofacCommandDispatcher

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

您可以一次注册所有命令处理程序,如下所示:

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

虽然有两个音符。首先,您可能将ICommandHandler<T>定义为逆变(使用in关键字),因为Resharper这样说,但这对于命令处理程序来说是个坏主意。命令和命令处理程序之间始终存在一对一映射,但定义in关键字,表明可以有多个实现。

其次,在我看来,拥有一个命令调度程序是一个坏主意,因为这可以隐藏消耗类命令处理程序有太多依赖关系这一事实,这表明违反了单一责任原则。此外,使用这样的调度程序推迟创建对象图的一部分(命令处理程序的一部分),直到命令被实际执行(与消费者被解析时相反)。这使得验证容器配置变得更加困难。直接注入命令处理程序时,您确定可以在解析配置中的根类型时解析整个对象图。定义命令很容易但忘记创建相应的命令处理程序,因此您需要为此添加单元测试以检查每个命令是否具有相应的处理程序。如果您同时删除调度程序,则可以避免编写此类测试。

答案 1 :(得分:2)

假设您ConcreteCommand : ICommanConcreteCommandHandler : ICommandHandler<ConcreteCommand>使用RegisterType这样的方法:

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

然后注入你的处理程序:

private ICommandHandler<ConcreteCommand> concreteCommandHandler;

另请参阅automatic assembly types registration Autofac功能。

如果您想通过ICommandHandler实施解决ICommand,那么工厂注册可以提供帮助。注册Func<Type, ICommandHandler>或定义将解析适当命令处理程序的特殊类。