Ninject - связать список типов

Я использую Ninject, Что я хочу сделать, это нанести на карту List типа, а затем внедрить его в конструктор моего класса:

private readonly IList<IDispatchFilter> m_Filters;

public DispatchFilteringManager(IList<IDispatchFilter> filters)
{
    m_Filters = filters;
}

Я пробовал это связывание:

Bind<IList<IDispatchFilter>>()
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    Bind<IDispatchFilter>().To<WindowsXpFilter>();

    IList<IDispatchFilter> result = context.Kernel.GetAll<IDispatchFilter>().ToList();

    return result;
}

Но в моем конструкторе я получаю пустой List,

Я не видел, чтобы найти решение для этой простой задачи.

2 ответа

Решение

Изменить:

Bind<IList<IDispatchFilter>>()
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    Bind<IDispatchFilter>().To<WindowsXpFilter>();
    ...
}

Для того, чтобы:

Bind<IDispatchFilter>().To<WindowsXpFilter>();
Bind<IList<IDispatchFilter>>()
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    ...
}

Объяснение:

Метод, который мы используем в .ToMethod обязательный для скажем T будет выполнен только когда мы позвоним container.Get<T> и не до этого.

Когда Ninject пытается решить IList<IDispatchFilter>> в bindDecoyDispatchFilters Метод, он ищет все привязки для IDispatchFilter зарегистрирован до этого и не находит ни одного. Поэтому параметр ctor разрешается как пустая коллекция.

Причина, по которой вы не получаете никаких элементов в конструкторе, к сожалению, связана с тем, как работает Ninject Multi Injection. Разрешение Ninject IList<T> кажется не интуитивно ищет все (самостоятельно) зарегистрированные <T> и вводить их в ваш класс, принимая IList<T>вместо того, чтобы фактически использовать явно зарегистрированный метод для разрешения IList<T>,

В следствии, bindDecoyDispatchFilters (как связано .ToMethod(bindDecoyDispatchFilters)) никогда не будет вызываться, поскольку Ninject вместо этого разрешит IList<T> на основании зарегистрированных типов T, (Это легко проверить - поставить точку останова или Assert.Fail() внутри метода - он никогда не вызывается).

Так что если

Bind<IDispatchFilter>().To<WindowsXpFilter>();

единственный IDispatchFilter который когда-либо должен быть решен в IList, тогда вы можете отказаться от регистрации, а в соответствии с @Fabjan's, непосредственно зарегистрироваться Bind<IDispatchFilter>().To<WindowsXpFilter>();, Многократный впрыск решит это как единственный элемент в IList<T> перешел к вашему конструктору.

Затем вы можете удалить IList<T> в целом:

Bind<IList<IDispatchFilter>>() ... remove 
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

а также бросить bindDecoyDispatchFilters метод полностью.

Однако, если список фильтров изменяется после начальной загрузки, и вам нужен динамический фабричный метод для возврата доступных фильтров вашим конструкторам, то вы можете прибегнуть к такому хаку.

В качестве альтернативы, если у вас мало классов, зависящих от IList<> Вы также можете явно зарегистрировать каждый класс, который снова имеет приоритет над множественным внедрением, поэтому код начальной загрузки становится:

kernel.Bind<ResolveMe>()
     .ToSelf()
     .WithConstructorArgument<IEnumerable<IDispatchFilter>>(bindDecoyDispatchFilters);

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    // Contract.Assert(1 == 0); // .. .ensure the method is called during resolution!
    context.Kernel.Bind<IDispatchFilter>().To<WindowsXpFilter>();
    return context.Kernel.GetAll<IDispatchFilter>().ToList();
}

Классы, которые я использовал для тестирования были:

public interface IDispatchFilter {}

public class WindowsXpFilter : IDispatchFilter { }

public class ResolveMe
{
    public IEnumerable<IDispatchFilter> Stuff { get; set; }

    public ResolveMe(IEnumerable<IDispatchFilter> stuff) { Stuff = stuff; }
}

И некоторые тесты:

  var y = kernel.Get<ResolveMe>();
  Assert.IsTrue(y.Stuff.Any());
Другие вопросы по тегам