Создание экземпляра с использованием Ninject с дополнительными параметрами в конструкторе

Я решил начать использовать Ninject и столкнуться с проблемой. Скажем, у меня есть следующий сценарий. У меня есть интерфейс IService и 2 класса, реализующие этот интерфейс. А также у меня есть класс, у которого есть конструктор, получающий IService и int. Как я могу создать экземпляр этого класса с помощью Ninject (я не хочу использовать этот int, я хочу передавать его каждый раз, когда я получаю экземпляр)?

Вот несколько примеров, иллюстрирующих ситуацию:

interface IService
{
    void Func();
}

class StandardService : IService
{
    public void Func()
    {
        Console.WriteLine("Standard");
    }
}

class AlternativeService : IService
{
    public void Func()
    {
        Console.WriteLine("Alternative");
    }
}


class MyClass
{
    public MyClass(IService service, int i)
    {
        this.service = service;
    }

    public void Func()
    {
        service.Func();
    }

    IService service = null;
}
class Program
{
    static void Main(string[] args)
    {
        IKernel kernel = new StandardKernel(new InlineModule(
            x => x.Bind<IService>().To<AlternativeService>(),
            x => x.Bind<MyClass>().ToSelf()));

        IService service = kernel.Get<IService>();

        MyClass m = kernel.Get<MyClass>();
        m.Func();
    }
}

Ответ 1

With.ConstructorArgument для этой цели существовал в 1.0. В 2.0 синтаксис несколько изменился: With.Parameters.ConstructorArgument с ninject 2.0

См. Ввести значение в вложенную зависимость для получения более подробной информации и примеров использования контекста, поставщиков и аргументов для более правильной передачи таких вещей.

EDIT: Поскольку Стивен решил сделать вид, что мой комментарий не имеет значения, я бы лучше дал понять, что я говорю с некоторыми примерами (для 2.0):

MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );

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

Если вы в состоянии определить параметр более глобальным способом, вы можете зарегистрировать поставщика и сделать это следующим образом:

class MyClassProvider : SimpleProvider<MyClass>
{
    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
    }
}

И зарегистрируйте его следующим образом:

x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )

NB бит CalculateINow() - это то место, где вы ввели бы логику, как в первом ответе.

Или сделайте это более сложным:

class MyClassProviderCustom : SimpleProvider<MyClass>
{
    readonly Func<int> _calculateINow;
    public MyClassProviderCustom( Func<int> calculateINow )
    {
        _calculateINow = calculateINow;
    }

    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
    }
}

Что вы зарегистрировали бы так:

x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( (  ) => new Random( ).Next( 9 ) ) )

UPDATE: более новые механизмы, которые демонстрируют значительно улучшенные шаблоны с меньшим количеством шаблонов, чем указано выше, воплощены в расширении Ninject.Extensions.Factory, см. https://github.com/ninject/ninject.extensions.factory/wiki

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

Последнее соображение состоит в том, что, поскольку вы не указали Using<Behavior>, он будет по умолчанию использовать по умолчанию, как указано/дефолтован в параметрах ядра (TransientBehavior в выборке), что может показать, что factory вычисляет i на лету спорный [например, если он был кэширован объектом]

Теперь, чтобы прояснить некоторые другие моменты в комментариях, которые FUDed и затушевываются. Некоторые важные вещи, которые следует учитывать при использовании DI, будь то Ninject или что-то еще:

  • Как можно больше сделать путем инъекции конструктора, поэтому вам не нужно использовать специальные атрибуты и трюки, специфичные для контейнера. Там есть хорошая запись в блоге, которая называется Отображается ваш контейнер IoC.

  • Свернуть код, идущий в контейнер, и попросить вещи - в противном случае ваш код будет связан с a) конкретным контейнером (который CSL может свести к минимуму). b) способ, которым весь ваш проект выложен. Есть хорошие сообщения в блогах о том, что CSL не делает то, что, по вашему мнению, делает. Эта общая тема называется Местонахождение службы и Инъекция зависимостей. ОБНОВЛЕНИЕ: см. http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx для подробного и полного обоснования.

  • Свести к минимуму использование статики и синглетов

  • Не предполагайте, что существует только один [глобальный] контейнер, и ему нужно просто требовать его, когда вам это нужно, как хорошая глобальная переменная. Правильное использование нескольких модулей и Bind.ToProvider() дает вам структуру для управления этим. Таким образом, каждая отдельная подсистема может работать сама по себе, и вы не будете иметь низкоуровневые компоненты, привязанные к компонентам верхнего уровня и т.д.

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

Теперь, если бы только Джоэл смог войти и действительно поставил меня прямо на какой красивый синтаксис и/или правильный способ сделать это!

UPDATE: хотя этот ответ, несомненно, полезен из числа найденных им номеров, я хотел бы сделать следующие рекомендации:

  • Вышеприведенное кажется немного устаревшим и, честно говоря, отражает много неполного мышления, которое почти смущает, поскольку читает Injection Dependency in.net - Запустите и купите его сейчас - это не только о DI, первая половина - полное обращение ко всем проблемам архитектуры, окружающим его от человека, который потратил слишком много времени, вися вокруг тега инъекции зависимостей.
  • Пойдите читать Mark Seemann топ-рейтинги здесь, на SO прямо сейчас - вы узнаете ценные методы от каждого