自动填充在CQRS CommandDispatcher中解析依赖关系
我正在尝试实现一个简单的CQRS应用程序示例。
I'm trying to implement a simple CQRS-application example.
这是我的命令部分的结构:
This is structure of my "Command" part:
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
。它向其处理程序发送一个命令。
This is interface ICommandDipatcher
. It dispatches a command to its handler.
public interface ICommandDispatcher
{
void Dispatch<TCommand>(TCommand command) where TCommand : ICommand;
}
这是 ICommandDispatcher
主要问题是通过 Autofac
命令类型获取必要的命令处理程序。
This is a default implementation of ICommandDispatcher
and the main problem is getting the necessary command handler by the type of command via 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);
}
}
解决 ICommandHanler
根据这种情况通过 Autofac 命令的类型
What is the best way to resolve implementation of ICommandHanler
by the type of command in that case via Autofac?
谢谢!
使用Autofac,您需要将 IComponentContext
注入调度程序。这样你可以调用回到容器来解析所需的命令处理程序:
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);
}
}
您可以注册 AutofacCommandDispatcher
如下:
builder.RegisterType<AutofacCommandDispatcher>().As<ICommandDispatcher>();
您可以一次性注册所有的命令处理程序,如下所示:
And you can register all your command handlers in one go as follows:
builder.RegisterAssemblyTypes(myAssembly)
.AsClosedTypesOf(typeof(ICommandHandler<>));
首先,您可能将 ICommandHandler< T>
定义为逆向(在关键字中的),因为Resharper说,但这对于命令处理程序来说是一个坏主意。在命令和命令处理程序之间始终存在一对一的映射,但是在
关键字中定义可以通信多个实现。
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.