Вводный массив интерфейсов в Ninject

Рассмотрим следующий код.

public interface IFoo { }


public class Bar
{
    public Bar(IFoo[] foos) { }
}


public class MyModule : NinjectModule
{
    public override void Load()
    {
        Bind<IFoo[]>().ToConstant(new IFoo[0]);
        // ToConstant() is just an example
    }
}


public class Program
{
    private static void Main(string[] args)
    {
        var kernel = new StandardKernel(new MyModule());
        var bar = kernel.Get<Bar>();
    }
}

Когда я пытаюсь запустить программу, я получаю следующее исключение.

Ошибка активации IFoo
Нет подходящих привязок, и тип не является самопереключаемым.
Путь активации:
2) Инъекция зависимости IFoo в параметр foos конструктора типа Bar
1) Запрос на бар

Как я могу вставить/привязать к массиву в Ninject?

Спасибо за ваше время.

Edit:
Мое приложение импортирует данные, созданные сторонним компонентом. В процессе импорта применяются различные фильтры (например, реализации различных интерфейсов фильтров). Правила фильтрации часто меняются, но слишком сложны для чистой конфигурации (и главного фильтра).

Я хочу, чтобы добавлять/редактировать фильтры как можно проще. У меня есть сборка, в которой находятся все реализации фильтра. Я попытался привязать каждый интерфейс фильтра к следующему методу (который предоставляет экземпляр каждой реализации этого типа фильтра). В основном я хочу избежать необходимости менять свой модуль Ninject при добавлении/удалении классов фильтров.

    private IEnumerable<TInterface> GetInterfaceImplementations<TInterface>(IContext context)
    {
        return GetType().Assembly.GetTypes()
            .Where(t => typeof (TInterface).IsAssignableFrom(t) && IsConcreteClass(t))
            .Select(t => Kernel.Get(t)).Cast<TInterface>();
    }

Я чувствую себя немного виноватым с точки зрения обхода механизма контейнеров DI. Это плохая практика? Существует ли обычная практика для таких целей?

Разрешение:
Я использую класс-оболочку в качестве предлагаемого bsnote.

Ответы

Ответ 1

Это в значительной степени повторение ответа @bsnote (который я + 1d), который может помочь понять, почему он работает таким образом.

Ninject (и другие каркасы DI/addin) имеют два различных средства:

  • понятие привязки к одной однозначной реализации службы (Get)
  • Объект, который позволяет получить набор сервисов [тот, который затем программно выбирает какой-либо из или агрегатов в некотором роде] (GetAll/ResolveAll в Ninject)

В вашем примере кода используется синтаксис, связанный с 2. выше. (например, в MEF, обычно используются аннотации [ImportMany], чтобы сделать это ясно)

Мне нужно будет посмотреть в образцах (посмотрите на источник - его очень короткий, чистый и удобный для поиска), чтобы найти обходной путь для этого.

Однако, как поясняет @bnote, один из способов рефакторинга вашего требования состоит в том, чтобы обернуть массив либо в контейнере, либо иметь объект, который вы запрашиваете (т.е. конструкцию типа factory или тип хранилища)

Возможно, вам будет полезно объяснить, каков ваш реальный случай - почему существует голый массив? Разумеется, существует коллекция элементов, которые попросят быть инкапсулированными, лежащими в основе всего этого - этот вопрос, безусловно, не приносит многого?

EDIT: Есть примеры примеров сканирования в расширениях, которые, как я предполагаю, будут атаковать многие вещи, которые вы пытаетесь сделать (в таких вещах, как StructureMap, такие вещи более интегрированы, что, очевидно, имеет плюсы и минусы минусы).

В зависимости от того, пытаетесь ли вы достичь соглашения по конфигурации или нет, вам может потребоваться придерживаться интерфейса маркера для каждого типа плагина. Тогда вы можете явно Bind каждый из них. В качестве альтернативы для CoC вы можете создать цикл обработки Module Load() по множеству создаваемых вами реализаций (т.е. Множество отдельных Get s) в вашем редактировании.

В любом случае, когда у вас есть несколько регистраций, вы можете с радостью либо "запросить" T[], либо IEnumerable<T> и получить полный набор. Если вы хотите достичь этого явно (т.е. Локатор сервисов и все, что он подразумевает), как и в случае с вами, вы можете использовать GetAll для их пакетной обработки, чтобы вы не выполняли цикл, который подразумевался в том, как вы это делали он.

Не уверен, что вы сделали это соединение или я что-то упустил. В любом случае, я надеюсь, он научил вас вводить код в вопросы, поскольку он говорит > 1000 слов: P

Ответ 2

Ninject поддерживает множественную инъекцию, которая разрешит вашу проблему. https://github.com/ninject/ninject/wiki/Multi-injection

public interface IFoo { }
public class FooA : IFoo {}
public class FooB : IFoo {}

public class Bar
{
    //array injected will contain [ FooA, FooB ] 
    public Bar(IFoo[] foos) { }
}

public class MyModule : NinjectModule
{
    public override void Load()
    {
        Bind<IFoo>().To<FooA>();
        Bind<IFoo>().To<FooB>();
        //etc..
    }
}

Ответ 3

Это была проблема для меня. Ninject вводит каждый элемент массива вместо самого массива, поэтому вы должны иметь сопоставление, определенное для типа элементов массива. На самом деле нет возможности сопоставить массив как тип с текущей версией Ninject. Решение состоит в создании обертки вокруг массива. Класс Lazy может использоваться, например, если он вам подходит. Или вы можете создать свою собственную оболочку.

Ответ 4

Поскольку Array реализует IReadOnlyList, работает следующее.

   // Binding
   public sealed class FooModule: NinjectModule 
   {
     public opverride void Load() 
     {
        Bind<IReadOnlyList<IFoo>>().ToConstant(new IFoo[0]);
      }
   }

   // Injection target
   public class InjectedClass {
      public InjectedClass(IReadOnlyList<IFoo> foos) { ;}
   }