1

If have the code below, implementing the command pattern. I want to store several commands in a list and afterwards pick them from the list, resolve a commandhandler and finally execute the command.

When implementing this I ran into the problem, that resolving a single command worked from Autofac but resolving commands stored in the list raised an exception telling me the commandhandler could not be found even when it's the same command as I resolve the commandhandler before.

    public static void ShowResolveProblem()
    {
        var action = new DisplayMessageAction("Hello");
        var actionhandler = GetActionHandler(action); // this works well

        var actions = new List<IAction>();
        actions.Add(action);
        actionhandler = GetActionHandler(actions[0]); // this throws exception
    }

And this is the resolving method

    private static IActionHandler<T> GetActionHandler<T>(T action) where T : IAction
    {
        var container = GetActionHandlerContainer();

        return container.Resolve<IActionHandler<T>>();
    }

Does anyone know how to get this running?

Cyril Durand
  • 15,834
  • 5
  • 54
  • 62
  • I have to mention that when adding the action to the list, the type information seems to be lost. When resolving the item taken from the list, Autofac does look for a IActionHandler instead of the former type. – Daniel Lehmann Feb 23 '15 at 12:20
  • It is a normal behavior. The first time you call `GetActionHandler`, `action` is of type `DisplayMessageAction` whereas the second time you call it with `action` of type `IAction`. So, it's like you call `.Resolve>` and `.Resolve>`. What are you trying to do ? why do you manually instanciate the action ? Do you have many IActionHandler implementation ? How do you register them ? – Cyril Durand Feb 23 '15 at 13:20
  • The action handlers are being instanciated by this call: builder.RegisterAssemblyTypes(assemblies).AsClosedTypesOf(typeof(IActionHandler<>)); What I actually want to do is, to have a workflow object that has a list of actions. With a run command on the workflow object the actions in the list shall be executed by the related Actionhandler. Is this the correct way to do this or do you know a different approach? – Daniel Lehmann Feb 23 '15 at 15:39
  • Sorry I meant 'registered' not 'instanciated' – Daniel Lehmann Feb 23 '15 at 15:45

1 Answers1

3

If you don't have the concrete type while resolving your actionHandler, you can get it using typeof(IActionHandler<>).MakeGenericType(action.GetType()).

To use your IActionHandler<T> without T you will have to create a new IActionHandler interface :

class Program
{
    static void Main(string[] args)
    {
        ContainerBuilder builder = new Autofac.ContainerBuilder();
        builder.RegisterAssemblyTypes(typeof(IActionHandler<>).Assembly)
               .AsClosedTypesOf(typeof(IActionHandler<>));

        IContainer container = builder.Build();

        List<IAction> actions = new List<IAction>();
        actions.Add(new DisplayMessageAction("Test1"));
        actions.Add(new DisplayMessageAction("Test2"));
        actions.Add(new BeepMessageAction(200, 200));

        foreach (IAction action in actions)
        {
            Type actionHandlerType = typeof(IActionHandler<>).MakeGenericType(action.GetType()); 
            IActionHandler actionHandler = (IActionHandler)container.Resolve(actionHandlerType);
            actionHandler.Execute(action);
        }
    }
}

public interface IAction { }
public interface IActionHandler
{
    void Execute(IAction action);
}
public interface IActionHandler<T> : IActionHandler
    where T : IAction
{
    void Execute(T IAction);
}

public abstract class ActionHandlerBase<T> : IActionHandler<T>
    where T : IAction
{
    void IActionHandler.Execute(IAction action)
    {
        this.Execute((T)action);
    }

    public abstract void Execute(T IAction);
}

public class DisplayMessageAction : IAction
{
    public DisplayMessageAction(String message)
    {
        this._message = message;
    }

    private readonly String _message;

    public String Message
    {
        get
        {
            return this._message;
        }
    }
}
public class DisplayMessageActionHandler : ActionHandlerBase<DisplayMessageAction>
{
    public override void Execute(DisplayMessageAction action)
    {
        Console.WriteLine(action.Message);
    }
}

public class BeepMessageAction : IAction
{
    public BeepMessageAction(Int32 frequency, Int32 duration)
    {
        this._frequency = frequency;
        this._duration = duration;
    }

    private readonly Int32 _frequency;
    private readonly Int32 _duration;

    public Int32 Frequency
    {
        get
        {
            return this._frequency;
        }
    }
    public Int32 Duration
    {
        get
        {
            return this._duration;
        }
    }
}
public class BeepMessageActionHandler : ActionHandlerBase<BeepMessageAction>
{
    public override void Execute(BeepMessageAction action)
    {
        Console.Beep(action.Frequency, action.Duration);
    }
}
Cyril Durand
  • 15,834
  • 5
  • 54
  • 62
  • Hi Cyril, thank you for the feedback. I was thinking of introducing a base class, but I thought there must be a simpler way. Anyway, the concrete implementations of the handlers and actions is pretty straight forward :-). – Daniel Lehmann Feb 25 '15 at 08:59