Могу ли я передать параметры конструктора методу Unity Resolve ()?

92

Я использую Microsoft Unity для внедрения зависимостей и хочу сделать что-то вроде этого:

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).

NotDan
источник

Ответы:

71

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

Это последняя капля здесь:

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 } });"
Существовать
источник
1
См. Также stackoverflow.com/questions/2813322/…
Майкл Фрейдгейм
6
ссылка unity.codeplex.com/SourceControl/changeset/view/33899 не активна
M.Kumaran
2
«Класс Microsoft.Practices.Unity.ParameterOverrides не имеет параметров типа». Я использую Unity 3.5; действителен ли этот код только для более старой версии Unity?
Thomas Levesque
Меня устраивает. Примечание. В вашем классе должен быть параметризованный конструктор с параметром «имя» и параметром «адрес». Foo(string name, int address) { ... }
adun
Использование Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis
38

<2 цента>

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

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

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

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

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

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

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

</ 2 цента>

Лассе В. Карлсен
источник
3
Я понимаю вашу точку зрения и согласен с вами, однако мне все еще нужно, чтобы экземпляры RepositoryA и RepositoryB имели одинаковый IDataContext, который должен отличаться от RepositoryC. Также обратите внимание, что IRepositoryA и IRepositoryB имеют свойство для IDataContext. Я немного обновлю образец кода.
NotDan
2
Отличный момент. Я собирался добавить строковый параметр в конструктор, но после просмотра этого момента решил сделать его полноценным объектом. На данный момент он состоит только из строки, но я уже вижу, как я могу добавить к нему больше полезных свойств
Сантош Бенджамин
9

Спасибо, ребята ... мой похож на пост "Exist". Увидеть ниже:

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

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });
Kwex
источник
5

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

В вашем случае этот объект должен быть зарегистрирован с именем, и для того же уровня вам понадобится 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")));
Trecenti
источник
4
Вы уверены в этом коде? Он не компилируется ... Resolveпринимает коллекцию ResolverOverrideи InjectionConstructorне является файлом ResolverOverride.
Thomas Levesque
Ага. Выглядит неправильно. Хотя в «Единстве» все должно было быть таким. Если имя параметра изменится, все сломается
Фрэнк К.
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

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

Самуэль Каррихо
источник
0

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

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

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

Джефф Фриц
источник