Чи можу я передати параметри конструктора в метод Unity Resolve ()?


91

Я використовую Unity від Microsoft для введення залежностей, і я хочу зробити щось подібне:

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

Відповіді:


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 } });"


5
посилання union.codeplex.com/SourceControl/changeset/view/33899 неактивне
M.Kumaran

2
"Клас 'Microsoft.Practices.Unity.ParameterOverrides' не має параметрів типу". Я використовую Unity 3.5; чи дійсний цей код лише для старішої версії Unity?
Thomas Levesque,

Це працює для мене. Примітка: Ваш клас повинен мати параметризований конструктор з параметром "name" та "address". Foo(string name, int address) { ... }
adun

Використання Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis

38

<2 центи>

Що робити, якщо згодом ви вирішите використовувати іншу послугу, яка вимагає більш-менш простого контексту?

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

Я пропоную вам також вирішити контекст, і я вважаю, що Unity повинен мати спосіб уникнути побудови 3 його екземплярів, або ви повинні розглянути фабричну службу, яка має спосіб побудувати об’єкт.

Наприклад, що, якщо згодом ви вирішите створити сховище, яке взагалі не покладається на традиційну базу даних, а замість цього використовує файл XML для створення фіктивних даних для тесту? Як би ви почали подавати вміст XML до цього конструктора?

IoC базується на коді роз'єднання, прив'язавши тип і семантику аргументів до конкретних типів, ви дійсно не зробили роз'єднання правильно, все ще існує залежність.

"Цей код може розмовляти з будь-яким типом сховища, можливо, якщо він реалізує цей інтерфейс .... О, і використовує контекст даних".

Тепер я знаю, що інші контейнери IoC підтримують це, і я мав це у своїй першій власній версії, але, на мій погляд, це не відповідає кроку вирішення.

</ 2 центи>


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

2
Чудовий момент. Я збирався додати параметр рядка до конструктора, але після перегляду цього пункту я вирішив зробити його повномасштабним об’єктом. На даний момент він складається лише з рядка, але я вже бачу, як я міг би додати до нього більше корисних властивостей
Сантош Бенджамін

9

Дякую, хлопці ... моя подібна до публікації "Існує". Дивись нижче:

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

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });

5

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

У вашому випадку цей Об'єкт повинен бути зареєстрований з Іменем, і для того ж самого підсумку вам потрібен ContainerControllLifeTimeManager (), як 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.
Thomas Levesque,

Так, це виглядає неправильно. Хоча єдність мала б так його розробити. Якщо ім'я параметра змінюється, все ламається
Frank Q.

3

Дуже коротка відповідь: ні. На даний момент Unity не має можливості передавати в конструктор параметри, які не є постійними або ін’єкційними, які мені вдалося знайти. IMHO - це найбільше, чого не вистачає, але я думаю, що це за дизайном, а не за упущенням.

Як зазначає Джефф Фріц, ви теоретично можете створити власний менеджер життя, який знає, який екземпляр контексту вводити в різні типи, але це рівень жорсткого кодування, який, здається, усуває мету використання 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.