我有以下内容:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我想使用扫描来注册ICommandHandler <>,以便在容器中获得以下类型:
ICommandHandler<AddUser> 具体型 AddUserHandlerICommandHandler<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);
}
}
Run Code Online (Sandbox Code Playgroud)
我希望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;
}
}
Run Code Online (Sandbox Code Playgroud)
由于这最初是一个StructureMap问题,这里是扫描添加这个:
Scan(x =>
{
x.AssemblyContainingType<MyConcreteHandler>();
x.IncludeNamespaceContainingType<MyConcreteHandler>();
x.AddAllTypesOf<ICommandHandler>();
x.WithDefaultConventions();
});
Run Code Online (Sandbox Code Playgroud)
这使我能够在我的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);
}
}
Run Code Online (Sandbox Code Playgroud)
这使我能够执行支持AddUser的CommandHandler(如AddUserHandler),但我也能够执行支持ICommand的处理程序(如AuditTrailHandler).
这太好了!
要将所有命令处理程序注入到命令调度程序中,请创建一个派生自的新的非通用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);
}
}
Run Code Online (Sandbox Code Playgroud)
这将使您的命令调度程序具有对 的构造函数依赖项IEnumerable<ICommandHandler>,StructureMap 将自动填充该构造函数。
在 SendCommand 中,您有两种方法来获取适当的处理程序集。基于类型的简单过滤器:
commandHandlers.OfType<ICommandHandler<T>>
Run Code Online (Sandbox Code Playgroud)
CanHandle(object command)或者在接口中添加一个ICommandHandler:
commandHandlers.Where(x => x.CanHandle(command))
Run Code Online (Sandbox Code Playgroud)
第二种方法需要更多代码,但允许您通过不仅仅是类型来附加处理程序,从而为您提供了更多的灵活性。这可能会让你的第一个问题更容易解决,因为你AuditTrailHandler总是true从CanHandle.