2011-01-19 61 views
8

我有以下几点:StructureMap对所有可能的具体实现注册泛型类型

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 <>让我得到以下类型的容器:

  • ICommandHandler<AddUser>与具体类型AddUserHandler
  • ICommandHandler<AddUser>与具体类型​​

我已经试过这与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而不是容器中,但我认为这对我现在的结构是不可能的。如果你有任何想法,证明我错了。

编辑 - 解决

我添加了一个非通用接口,我的通用接口实现,然后我还添加了一个抽象CommandHandler<T>所以我没有实现CanHandle或执行(对象)的所有方法我的处理程序。

这是工作结构:

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

这使我能够在注入一个IEnumerable我的CommandDispatcher并执行如下:

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

这使我能够执行Com MandHandlers支持AddUser(就像AddUserHandler),但我也能够执行支持ICommand的处理程序(如AuditTrailHandler)。

这很甜!

回答

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中,您有两种获取相应处理程序的方法。基于A型简单的过滤器:

commandHandlers.OfType<ICommandHandler<T>> 

或添加CanHandle(object command)ICommandHandler接口:

commandHandlers.Where(x => x.CanHandle(command)) 

第二种方法需要更多的代码,但是允许您连接处理给你多一点灵活性不仅仅是类型。这可能会使您的​​始终返回trueCanHandle更容易解决您的第一个问题。

+0

太棒了! 我以前有过CanHandle方法,但是因为我认为StructureMap魔术可以帮我做到这一点,所以将其删除。 我甚至进一步做了一个抽象的CommandHandler ,并将它放在泛型接口和具体类之间,这样我就可以从CanHandle方法中受益,而无需在所有派生类中实现它。 真棒,谢谢! – 2011-01-19 16:11:59