Создание экземпляра с использованием 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
См. Добавление значения во введенную зависимость для получения дополнительной информации и примеров того, как использовать контекст, поставщиков и аргументы для более корректной передачи подобных вещей.
РЕДАКТИРОВАТЬ: Поскольку Стивен решил притвориться, что мой комментарий не имеет значения, я лучше поясню, что я говорю, с некоторыми примерами (для 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 ) ) )
ОБНОВЛЕНИЕ: более новые механизмы, которые показывают намного улучшенные образцы с меньшим количеством образцов, чем выше, воплощены в Ninject.Extensions.Factory
расширение см.: https://github.com/ninject/ninject.extensions.factory/wiki
Как указывалось ранее, если вам нужно каждый раз передавать разные параметры и у вас есть несколько уровней в графе зависимостей, вам может потребоваться сделать что-то подобное.
Последнее соображение заключается в том, что, поскольку вы не указали Using<Behavior>
, он будет по умолчанию к значению по умолчанию, как указано / по умолчанию в опциях для ядра (TransientBehavior
в образце), что может сделать факт, что фабрика рассчитывает i
на лету [например, если объект кэшируется]
Теперь, чтобы уточнить некоторые другие моменты в комментариях, которые FUDED и глоссарий. Некоторые важные вещи, которые следует учитывать при использовании DI, будь то Ninject или что-то еще:
Сделайте как можно больше за счет внедрения в конструктор, чтобы вам не нужно было использовать специфичные для контейнера атрибуты и приемы. Есть хорошая запись в блоге, которая называется Your IoC Container показывает.
Минимизируйте код, идущий к контейнеру и запрашивая материал - иначе ваш код связан с a) конкретным контейнером (который может свернуть CSL) b) способом, которым выложен весь ваш проект. В этом блоге есть хорошие сообщения о том, что CSL не делает то, что вы думаете. Этот общий раздел называется расположением службы и внедрением зависимости. ОБНОВЛЕНИЕ: см. http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx для подробного и полного обоснования.
Минимизировать использование статики и синглетонов
Не думайте, что есть только один [глобальный] контейнер, и что можно просто требовать его всякий раз, когда вам это нужно, как хорошую глобальную переменную. Правильное использование нескольких модулей и
Bind.ToProvider()
дает вам структуру для управления этим. Таким образом, каждая отдельная подсистема может работать самостоятельно, и у вас не будет компонентов низкого уровня, привязанных к компонентам верхнего уровня и т. Д.
Если кто-то захочет заполнить ссылки на блоги, на которые я ссылаюсь, я был бы признателен за это (хотя они все уже связаны с другими публикациями на SO, так что все это просто дублирование пользовательского интерфейса, введенное с целью избежать путаницы с вводящим в заблуждение ответом.)
Теперь, если бы только Джоэл мог прийти и по-настоящему объяснить мне, какой хороший синтаксис и / или правильный способ сделать это!
ОБНОВЛЕНИЕ: Хотя этот ответ явно полезен из числа полученных голосов, я бы хотел дать следующие рекомендации:
- Вышеприведенное выглядит немного устаревшим и, честно говоря, отражает много неполного мышления, которое почти смущает после прочтения Dependency Injection в.net - беги и покупай сейчас - это не только DI, первая половина - полное рассмотрение вся архитектура касается его окружения от человека, который слишком много времени проводил здесь, висящий вокруг тега внедрения зависимости.
- Читайте лучшие посты Mark Seemann здесь на SO прямо сейчас - вы узнаете ценные приемы у каждого