Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StructureMap register generic types against all possible concrete implementations

I have the following:

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

I would like to use scanning to register the ICommandHandler<> so that I get the following types in the container:

  • ICommandHandler<AddUser> with concrete type AddUserHandler
  • ICommandHandler<AddUser> with concrete type AuditTrailHandler

I have tried this with an implementation of IRegistrationConvention and at one point I had it working but I just cannot get my head around how I did it.

The goal is to be able to execute several handlers for a specific ICommand implementation like so:

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

I want the AuditTrailHandler<ICommand> to execute for all concrete implementations of ICommand, hence the need to register them for all types of ICommand.

Secondary objective would be if I could inject a collection of ICommandHandlers<ICommand> into my CommandDispatcher instead of the container, but I think that's impossible with the structure I have now. Prove me wrong if you have any ideas.

EDIT - solved

I added a non generic interface that my generic interface implements and then I also added an Abstract CommandHandler<T> so I don't have to implement the CanHandle or Execute(object) methods in all my handlers.

This is the working structure:

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

And since this originally was a StructureMap question, here is the Scan to add this:

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

This makes me able to inject an IEnumerable in my CommandDispatcher and execute like so:

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

This makes me able to execute CommandHandlers that support AddUser (like the AddUserHandler), but I'm also able to execute a handler that support ICommand (like the AuditTrailHandler).

This is sweet!

like image 639
Mikael Östberg Avatar asked Jan 19 '11 09:01

Mikael Östberg


1 Answers

To have all of the command handlers injected into the command dispatcher, create a new, non-generic ICommandHandler interface which ICommandHandler<T> derives from. It has an Execute method which takes an object. The downside is that each of your command handlers has to implement that method to call the typed overload:

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

This will enable your command dispatcher to have a constructor dependency on IEnumerable<ICommandHandler>, which StructureMap will automatically populate.

In SendCommand you have 2 ways of getting the appropriate set of handlers. A simple filter based on type:

commandHandlers.OfType<ICommandHandler<T>>

or add a CanHandle(object command) to the ICommandHandler interface:

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

The second approach requires more code, but gives you a little more flexibility by allowing you to attach handler by more than just type. This may make solving your first problem easier, by making your AuditTrailHandler always return true from CanHandle.

like image 126
Joshua Flanagan Avatar answered Nov 05 '22 17:11

Joshua Flanagan