78

Я использую Unity Microsoft для инъекции зависимостей, и я хочу сделать что-то вроде этого:Могу ли я передать параметры конструктора методу Unity's Resolve()?

IDataContext context = _unityContainer.Resolve<IDataContext>(); 
var repositoryA = _unityContainer.Resolve<IRepositoryA>(context); //Same instance of context 
var repositoryB = _unityContainer.Resolve<IRepositoryB>(context); //Same instance of context 

IDataContext context2 = _unityContainer.Resolve<IDataContext>(); //New instance 
var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(context2); 

RepositoryA и RepositoryB оба имеют конструктор, который принимает IDataContext параметр, и я хочу Unity, чтобы инициализировать репозиторий с контекстом что я его передаю. Также обратите внимание, что IDataContext не зарегистрирован в Unity (я не хочу 3 экземпляра IDataContext).

ответ

62

на сегодняшний день они добавили эту функциональность:

Это в последней капли здесь:

http://unity.codeplex.com/SourceControl/changeset/view/33899

Обсуждение на нем здесь:

http://unity.codeplex.com/Thread/View.aspx?ThreadId=66434

Пример:

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });" 
+0

См. Также http://stackoverflow.com/questions/2813322/unity-2-0-how-to-use -resolve-with-resolveroverride –

+5

ссылка http://unity.codeplex.com/SourceControl/changeset/view/33899 не активна –

+2

«Класс» Microsoft.Practices.Unity.ParameterOverrides «не имеет параметров типа». Я использую Unity 3.5; этот код действителен только для более старой версии Unity? –

33

< 2 цента>

Что делать, если позже вы решите использовать другую услугу, которая требует более или менее, чем просто контекст?

Проблема с параметрами конструктора и IoC заключается в том, что параметры в конечном счете привязаны к используемому конкретному типу, а не к части контракта, который определяет интерфейс службы.

Мое предложение состояло в том, что вы либо разрешаете контекст, и я считаю, что Unity должно иметь способ избежать создания трех экземпляров, или вы должны рассмотреть фабричную службу, которая имеет способ построить объект.

Например, если позднее вы решите построить репозиторий, который вообще не полагается на традиционную базу данных, а вместо этого использует XML-файл для создания фиктивных данных для теста? Как вы могли бы поместить XML-контент в этот конструктор?

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

«Этот код может разговаривать с любым типом репозитория, если он реализует этот интерфейс ... Ох и использует контекст данных».

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

</2 цента>

+3

Я вижу вашу точку и с вами согласен, но я все еще нужен RepositoryA и экземпляры RepositoryB, чтобы иметь такую ​​же IDataContext, который должен быть иным, чем RepositoryC. Также обратите внимание, что IRepositoryA и IRepositoryB имеют свойство для IDataContext. Я немного обновлю пример кода. – NotDan

+2

Отличная точка. Я собирался добавить строковый параметр в конструктор, но после просмотра этого пункта я решил сделать его полностью раздутым. Он состоит только из строки в этой точке, но я уже вижу, как я могу добавить к ней дополнительные полезные свойства. –

0

NotDan, я думаю, вы сами ответили на свой вопрос в комментариях к lassevk.

Во-первых, я бы использовал LifetimeManager для управления жизненным циклом и количеством экземпляров IDataContext, которые создает Unity.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Похоже, что объект ContainerControlledLifetimeManager предоставит вам управление инстанциями, которое вам нужно. Используя этот LifetimeManager, Unity должен добавить тот же экземпляр IDataContext ко всем объектам, для которых требуется зависимость IDataContext.

3

Очень короткий ответ: нет. В настоящее время Unity не имеет возможности передавать параметры в конструктор, которые не являются постоянными или инъецированными, которые я смог найти. ИМХО, это самая большая вещь, которую не хватает, но я думаю, что это дизайн, а не упущение.

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

Вы можете сделать небольшой шаг назад из полного DI и сделать ваши реализации хранилища ответственными за создание своих собственных контекстов данных. Контекст экземпляр все еще может быть разрешен из контейнера, но логика принятия решения о том, какой из них использовать, должна была бы войти в реализацию репозитория. Конечно, это не так чисто, но это избавит от проблемы.

1

Другой вариант, вы можете использовать (на самом деле не знаю, если это хорошая практика, или нет) создает два контейнера и регистрации экземпляра для каждого:

IDataContext context = _unityContainer.Resolve<IDataContext>(); 
_unityContainer.RegisterInstance(context); 
var repositoryA = _unityContainer.Resolve<IRepositoryA>(); //Same instance of context 
var repositoryB = _unityContainer.Resolve<IRepositoryB>(); //Same instance of context 


//declare _unityContainer2 
IDataContext context2 = _unityContainer2.Resolve<IDataContext>(); //New instance 
_unityContainer2.RegisterInstance(context2); 
var repositoryA2 = _unityContainer2.Resolve<IRepositoryA>(context2); //will retrieve the other instance 

надеюсь, что это помогает тоже

7

Вы можете использовать InjectionConstructor/InjectionProperty/InjectionMethod в зависимости от вашего Injection архитектуры внутри ResolvedParameter < T> ("имя"), чтобы получить экземпляр предварительно зарегистрированного объекта в контейнере.

В вашем случае этот объект должен быть зарегистрирован с именем, и для того же самого состояния вам необходимо ContainerControlledLifeTimeManager() в качестве LifeTimeManager.

_unityContainer.RegisterType<IDataContext,DataContextA>("DataContextA", new ContainerControlledLifeTimeManager()); 
_unityContainer.RegisterType<IDataContext,DataContextB>("DataContextB"); 

    var repositoryA = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA"))); 

    var repositoryB = _unityContainer.Resolve<IRepositoryB>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA"))); 

    var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextB"))); 
+4

Вы уверены, что хотите ввести код? Он не компилируется ... 'Resolve' берет коллекцию' ResolverOverride', а 'InjectionConstructor' не является' ResolverOverride'. –

+0

Yup Это выглядит неправильно. Хотя единство должно было спроектировать его таким образом. Если имя параметра изменяется, все ломается –

5

Спасибо, ребята ... моя похожа на сообщение от «Exist». Смотрите ниже:

 IUnityContainer container = new UnityContainer(); 
     container.LoadConfiguration(); 

     _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[] 
     { 
      new ParameterOverride("activeDirectoryServer", "xyz.adserver.com") 
     }); 
Смежные вопросы