Autofac解决了CQRS CommandDispatcher中的依赖关系

本文关键字:依赖 关系 CommandDispatcher 解决 CQRS Autofac | 更新日期: 2023-09-27 18:28:04

我正在尝试实现一个简单的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实现的最佳方法是什么?

谢谢!

Autofac解决了CQRS CommandDispatcher中的依赖关系

使用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关键字表明可以有多个实现。

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

假设您有ConcreteCommand : ICommanConcreteCommandHandler : ICommandHandler<ConcreteCommand>,请使用RegisterType方法,如下所示:

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

然后注入你的处理程序:

private ICommandHandler<ConcreteCommand> concreteCommandHandler;

还可以查看自动装配类型注册Autofac功能。

如果您想通过ICommand实现来解决ICommandHandler问题,那么工厂注册会有所帮助。注册Func<Type, ICommandHandler>或定义将解析适当命令处理程序的特殊类。