2011-01-19 15 views
8

Tengo el siguiente:StructureMap registrar tipos genéricos contra todas las posibles implementaciones concretas

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

me gustaría utilizar el escaneado para registrar el ICommandHandler <> de modo que consiga los siguientes tipos en el contenedor:

  • ICommandHandler<AddUser> con el tipo concreto AddUserHandler
  • ICommandHandler<AddUser> con el tipo concreto AuditTrailHandler

He intentado esto con una implementación de IRegistrationConvention y en un momento lo tuve funcionando, pero no entiendo cómo lo hice.

El objetivo es ser capaz de ejecutar varios controladores para una aplicación específica ICommand así:

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

Quiero que el AuditTrailHandler<ICommand> a ejecutar para todas las implementaciones concretas de ICommand, de ahí la necesidad de registrarlos para todos tipos de ICommand.

El objetivo secundario sería si pudiera inyectar una colección de ICommandHandlers<ICommand> en mi CommandDispatcher en lugar del contenedor, pero creo que eso es imposible con la estructura que tengo ahora. Demuestre que estoy equivocado si tiene alguna idea.

EDITAR - resuelto

que añade una interfaz genérica que no implementa la interfaz mis genérico y luego tambien tiene añadido un extracto CommandHandler<T> así que no tienen que poner en práctica el CanHandle o Ejecutar (objeto) en todos los métodos mis manejadores.

Esta es la estructura de trabajo:

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

Y puesto que este principio era una pregunta StructureMap, aquí es la función Escanear a añadir lo siguiente:

Scan(x => 
     { 
      x.AssemblyContainingType<MyConcreteHandler>(); 
      x.IncludeNamespaceContainingType<MyConcreteHandler>(); 
      x.AddAllTypesOf<ICommandHandler>(); 
      x.WithDefaultConventions(); 
     }); 

Esto me hace capaz de inyectar un IEnumerable en mi CommandDispatcher y ejecutar así:

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

Esto me permite ejecutar Com mandHandlers que son compatibles con AddUser (como AddUserHandler), pero también puedo ejecutar un controlador que admita ICommand (como AuditTrailHandler).

Esto es dulce!

Respuesta

4

Para tener todos los controladores de comandos inyectados en el despachador de comandos, cree una nueva interfaz no genérica ICommandHandler de la que deriva ICommandHandler<T>. Tiene un método Execute que toma un objeto. La desventaja es que cada uno de sus controladores de comandos tiene que aplicar ese método a llamar a la sobrecarga escrito:

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

Esto permitirá a su despachador de comandos a tener una dependencia constructor en IEnumerable<ICommandHandler>, que StructureMap rellenará automáticamente.

En SendCommand tiene 2 formas de obtener el conjunto adecuado de controladores. Un simple filtro en función del tipo:

commandHandlers.OfType<ICommandHandler<T>> 

o agregar un CanHandle(object command) a la ICommandHandler interfaz:

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

El segundo enfoque requiere más código, pero le da un poco más de flexibilidad que le permite conectar manipulador por algo más que escribir. Esto puede facilitar la solución de su primer problema, haciendo que su AuditTrailHandler siempre devuelva true desde CanHandle.

+0

¡Eso es brillante! Tuve el método CanHandle anteriormente, pero lo eliminé porque pensé que la magia de StructureMap podría hacerlo por mí. Incluso fui un poco más allá e hice un CommandHandler abstracto y puse eso entre la interfaz genérica y la clase concreta, de modo que puedo beneficiarme del método CanHandle sin tener que implementarlo en todas las clases derivadas. ¡Increíble, gracias! –

Cuestiones relacionadas