#.net #ninject #service-locator
#.net #ninject #service-locator
Вопрос:
Я, наконец, получил некоторое представление о том, как Ninject обрабатывает DI, но столкнулся со следующей проблемой:
Давайте рассмотрим, что у нас есть класс, который принимает два объекта WCF ServiceHost в качестве параметров конструктора:
public ActivitySinkServer(IDataProvider dataProvider, ServiceHost posClients, ServiceHost activitySinkOperatorClients)
Сначала у меня была только одна зависимость ServiceHost, поэтому я легко обработал привязку следующим образом:
public class CommunicationModule: NinjectModule
{
public override void Load()
{
Bind<POSClient>().ToSelf().WithConstructorArgument("posManager", Kernel.Get<POSManager>());
this.Bind<ServiceHost>().ToMethod(ctx => ctx.Kernel.Get<NinjectServiceHost>(new ConstructorArgument("singletonInstance", c => c.Kernel.Get<POSClient>())));
}
}
В этом сценарии мой ActivitySinkServer
мог бы разрешить его ServiceHost
зависимость с помощью NinjectServiceHost
, инициализированного одноэлементным объектом.
Теперь, когда у меня есть две зависимости ServiceHost, как я могу указать Ninject, какую из них передавать в какой параметр конструктора, все еще имея мой внутренний код, о котором Ninject не знает. (Я знаю, что мог бы использовать атрибуты Ninject и другие материалы из руководства).
Обновить:
Я пошел дальше и просто использовал
.When(request => request.Target.Name == "posClients");
.When(request => request.Target.Name == "activitySinkOperatorClients");
чтобы явно указать имена переменных целевого конструктора. Не вижу в этом никакого вреда. Однако, если у кого-то есть более элегантный и объектно-ориентированный подход — вы можете ответить.
Ответ №1:
Способ, которым вы это делаете, на 100% хорош; более «элегантный» способ сделать это — использовать именованные привязки или метаданные.
Кстати, в этом случае гораздо лучше не использовать singletonInstance
конструктор ServiceHost
, потому что, если вы инициализируете его таким образом, WCF не позволит вам использовать какой-либо другой режим создания экземпляра (например, PerCall
). Пусть Ninject и WCF обрабатывают создание экземпляра и вместо этого используют конструктор на основе типов.
Примером именованной привязки может быть:
class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ServiceHost>().To<NinjectServiceHost>().Named("POS")
.WithConstructorArgument("serviceType", typeof(PosService))
.WithConstructorArgument("baseAddresses", new Uri[0]);
Bind<ServiceHost>().To<NinjectServiceHost>().Named("ActivitySink")
.WithConstructorArgument("serviceType", typeof(ActivitySink))
.WithConstructorArgument("baseAddresses", new Uri[0]);
}
}
public class Server
{
private readonly ServiceHost posHost;
private readonly ServiceHost activitySink;
public Server(IDataProvider dataProvider,
[Named("POS")] posHost,
[Named("ActivitySink")] activitySink)
{
this.posHost = posHost;
this.activitySink = activitySink;
}
}
Обратите внимание, что инициализация baseAddresses
аргумента конструктора необходима Ninject для выбора правильной перегрузки. Ее new Uri[0]
конкретная инициализация просто приводит к тому, что по умолчанию выполняется поиск app.config
базовых адресов, поэтому не беспокойтесь о передаче пустого массива.
Хотя это связывает ваш Server
класс с самим Ninject, обычно ваши ServiceHost
экземпляры создаются в двоичном файле приложения, а не в библиотеке, поэтому связь не является проблемой.
Я предпочитаю этот подход When
синтаксису, потому что он с меньшей вероятностью сломается во время рефакторинга. Не исключено, что кто-нибудь однажды решит изменить имена параметров конструктора, и нет никаких визуальных указаний на то, что что-либо зависит от этих имен, и нет никакого способа для автоматического рефакторинга Visual Studio обнаружить эту зависимость.
Итак, ИМО, здесь лучше сделать зависимость явной, используя атрибуты; таким образом, если кто-то решит добавить третий узел службы позже, они сразу узнают, что им нужно добавить атрибут и обновить соответствующий модуль Ninject.