Ninject - связать список типов - PullRequest
0 голосов
/ 11 июня 2018

Я использую 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 ]

0 голосов
/ 11 июня 2018

Причина, по которой вы не получаете никаких элементов в конструкторе, к сожалению, связана с тем, как Ninject's 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<>, вы также можете явно зарегистрировать каждый класс, который снова имеет приоритет над мультиинжекцией, так что Bootstrappingкод становится:

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());
0 голосов
/ 11 июня 2018

Изменение:

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 разрешается как пустая коллекция.

...