StructureMap针对所有可能的具体实现注册泛型类型

Mik*_*erg 8 c# 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我想使用扫描来注册ICommandHandler <>,以便在容器中获得以下类型:

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

这太好了!

Jos*_*gan 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);
    }
}
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总是trueCanHandle.