Команды не могут быть решены с помощью Autofac при сохранении в списке

Если у вас есть приведенный ниже код, реализующий шаблон команды. Я хочу сохранить несколько команд в списке, а затем выбрать их из списка, разрешить обработчик команд и, наконец, выполнить команду.

При реализации этого я столкнулся с проблемой, что разрешение одной команды работало из Autofac, но разрешение команд, хранящихся в списке, вызвало исключение, сообщающее мне, что обработчик команд не может быть найден, даже если это та же команда, что и я разрешаю обработчик команд раньше.

    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
    }

И это метод разрешения

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

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

Кто-нибудь знает, как это запустить?


person Daniel Lehmann    schedule 23.02.2015    source источник
comment
Я должен упомянуть, что при добавлении действия в список информация о типе, похоже, теряется. При разрешении элемента, взятого из списка, Autofac ищет IActionHandler‹IAction› вместо прежнего типа.   -  person Daniel Lehmann    schedule 23.02.2015
comment
Это нормальное поведение. В первый раз, когда вы вызываете GetActionHandler, action имеет тип DisplayMessageAction, тогда как во второй раз вы вызываете его с action типа IAction. Итак, вы звоните .Resolve<IActionHandler<DisplayMessageAction>> и .Resolve<IActionHandler<IAction>>. Что ты пытаешься сделать ? почему вы вручную создаете действие? У вас много реализаций IActionHandler? Как вы их регистрируете?   -  person Cyril Durand    schedule 23.02.2015
comment
Обработчики действий создаются этим вызовом: builder.RegisterAssemblyTypes(assemblies).AsClosedTypesOf(typeof(IActionHandler‹›)); Что я на самом деле хочу сделать, так это иметь объект рабочего процесса со списком действий. С помощью команды запуска для объекта рабочего процесса действия в списке должны выполняться соответствующим обработчиком действий. Это правильный способ сделать это или вы знаете другой подход?   -  person Daniel Lehmann    schedule 23.02.2015
comment
Извините, я имел в виду «зарегистрировано», а не «создано».   -  person Daniel Lehmann    schedule 23.02.2015


Ответы (1)


Если у вас нет конкретного типа при разрешении вашего actionHandler, вы можете получить его с помощью typeof(IActionHandler<>).MakeGenericType(action.GetType()).

Чтобы использовать IActionHandler<T> без T, вам придется создать новый интерфейс IActionHandler:

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);
    }
}
person Cyril Durand    schedule 23.02.2015
comment
Здравствуйте Кирилл, спасибо за отзыв. Я думал ввести базовый класс, но подумал, что должен быть более простой способ. Во всяком случае, конкретная реализация обработчиков и действий довольно проста :-). - person Daniel Lehmann; 25.02.2015