我有以下内容:
public interface ICommand { }
public class AddUser : ICommand
{
public string Name { get; set; }
public string Password { get; set; }
}
public interface ICommandHandler<T> : IHandler<T> where T : ICommand
{
void Execute(T command);
}
public class AddUserHandler : ICommandHandler<AddUser>
{
public void Execute(AddUser command)
{
Console.WriteLine("{0}: User added: {1}", GetType().Name, command.Name);
}
}
public class AuditTrailHandler : ICommandHandler<ICommand>
{
public void Execute(ICommand command)
{
Console.WriteLine("{0}: Have seen a command of type {1}", GetType().Name, command.GetType().Name);
}
}
我想使用扫描来注册ICommandHandler&lt;&gt;这样我就可以在容器中获得以下类型:
ICommandHandler<AddUser>
的AddUserHandler
具体类型ICommandHandler<AddUser>
AuditTrailHandler
我已经尝试过IRegistrationConvention的实施,但有一次我让它工作但是我无法理解我是如何做到的。
目标是能够为特定的ICommand实现执行多个处理程序,如下所示:
// A method in CommandDispatcher
public void SendCommand<T>(T command) where T : ICommand {
var commandHandlers = container.GetAllInstances<ICommandHandler<T>>();
foreach (var commandHandler in commandHandlers) {
commandHandler.Execute(command);
}
}
我希望AuditTrailHandler<ICommand>
执行ICommand的所有具体实现,因此需要为所有类型的ICommand注册它们。
次要目标是,如果我可以将ICommandHandlers<ICommand>
的集合注入我的CommandDispatcher而不是容器,但我认为现在的结构是不可能的。如果你有任何想法,请证明我错了。
编辑 - 已解决
我添加了我的通用接口实现的非泛型接口,然后我还添加了一个Abstract CommandHandler<T>
,因此我不必在所有处理程序中实现CanHandle或Execute(对象)方法。
这是工作结构:
public interface ICommandHandler
{
void Execute(object command);
bool CanHandle(ICommand command);
}
public interface ICommandHandler<T> : ICommandHandler, IHandler<T> where T : ICommand
{
void Execute(T command);
}
public abstract class AbstractCommandHandler<T> : ICommandHandler<T> where T : ICommand
{
public abstract void Execute(T command);
public void Execute(object command)
{
Execute((T)command);
}
public virtual bool CanHandle(ICommand command)
{
return command is T;
}
}
因为这最初是一个StructureMap问题,所以这是扫描添加它:
Scan(x =>
{
x.AssemblyContainingType<MyConcreteHandler>();
x.IncludeNamespaceContainingType<MyConcreteHandler>();
x.AddAllTypesOf<ICommandHandler>();
x.WithDefaultConventions();
});
这使我能够在我的CommandDispatcher中注入一个IEnumerable并执行如下:
public void SendCommand<T>(T command) where T : ICommand
{
var commandHandlersThatCanHandle = commandHandlers.Where(c => c.CanHandle(command));
foreach (var commandHandler in commandHandlersThatCanHandle)
{
commandHandler.Execute(command);
}
}
这使我能够执行支持AddUser的CommandHandler(如AddUserHandler),但我也能够执行支持ICommand的处理程序(如AuditTrailHandler)。
这很好吃!
答案 0 :(得分:4)
要将所有命令处理程序注入命令调度程序,请创建ICommandHandler
派生自的新的非通用ICommandHandler<T>
接口。它有一个Execute方法,它接受一个对象。缺点是每个命令处理程序都必须实现该方法来调用类型化的重载:
public class AddUserHandler : ICommandHandler<AddUser>
{
public void Execute(object command)
{
Execute((AddUser)command);
}
public void Execute(AddUser command)
{
Console.WriteLine("{0}: User added: {1}", GetType().Name, command.Name);
}
}
这将使您的命令调度程序对IEnumerable<ICommandHandler>
具有构造函数依赖关系,StructureMap将自动填充。
在SendCommand中,您有两种获取适当处理程序的方法。一个基于类型的简单过滤器:
commandHandlers.OfType<ICommandHandler<T>>
或向CanHandle(object command)
界面添加ICommandHandler
:
commandHandlers.Where(x => x.CanHandle(command))
第二种方法需要更多代码,但允许您通过不仅仅键入来附加处理程序,从而为您提供更多灵活性。通过让AuditTrailHandler
始终从true
返回CanHandle
,这可以让您更轻松地解决第一个问题。