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