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

时间:2011-01-19 09:07:54

标签: 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);
    }
}

我想使用扫描来注册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)。

这很好吃!

1 个答案:

答案 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,这可以让您更轻松地解决第一个问题。