Czy mogę przekazać parametry konstruktora do metody Resolve () aparatu Unity?

92

Używam Unity firmy Microsoft do wstrzykiwania zależności i chcę zrobić coś takiego:

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

RepositoryAi RepositoryBoba mają konstruktor, który przyjmuje IDataContextparametr, i chcę, aby Unity zainicjował repozytorium z kontekstem, który mu przekazuję. Zwróć również uwagę, że IDataContextnie jest zarejestrowany w Unity (nie chcę 3 wystąpień IDataContext).

NotDan
źródło

Odpowiedzi:

71

Na dzień dzisiejszy dodali tę funkcjonalność:

Jest w najnowszej kropli tutaj:

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

Dyskusja na ten temat tutaj:

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

Przykład:

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
Istnieć
źródło
1
Zobacz także stackoverflow.com/questions/2813322/…
Michael Freidgeim
6
link unity.codeplex.com/SourceControl/changeset/view/33899 nie jest aktywny
M.Kumaran
2
„Klasa„ Microsoft.Practices.Unity.ParameterOverrides ”nie ma parametrów typu”. Używam Unity 3.5; czy ten kod jest ważny tylko dla starszej wersji Unity?
Thomas Levesque,
Mi to pasuje. Uwaga: Twoja klasa musi mieć sparametryzowany konstruktor z parametrami „nazwa” i „adres”. Foo(string name, int address) { ... }
dodane
Korzystanie z Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis
38

<2 centy>

A jeśli później zdecydujesz się skorzystać z innej usługi, która wymaga więcej lub mniej niż tylko kontekstu?

Problem z parametrami konstruktora i IoC polega na tym, że parametry są ostatecznie powiązane z używanym konkretnym typem, a nie są częścią kontraktu definiowanego przez interfejs usługi.

Moją sugestią byłoby, abyś albo rozwiązał również kontekst, i uważam, że Unity powinna mieć sposób, abyś uniknął konstruowania 3 jego instancji, albo powinieneś rozważyć usługę fabryczną, która ma sposób na zbudowanie obiektu.

Na przykład, co się stanie, jeśli później zdecydujesz się zbudować repozytorium, które w ogóle nie opiera się na tradycyjnej bazie danych, ale zamiast tego używa pliku XML do tworzenia fikcyjnych danych do testu? Jak zabrałbyś się do dostarczania zawartości XML do tego konstruktora?

IoC opiera się na odsprzęganiu kodu, poprzez wiązanie typu i semantyki argumentów z konkretnymi typami, naprawdę nie wykonałeś poprawnie oddzielenia, nadal istnieje zależność.

„Ten kod może komunikować się z dowolnym typem repozytorium, o ile implementuje ten interfejs… Och, i używa kontekstu danych”.

Teraz wiem, że inne kontenery IoC obsługują to i miałem to również w mojej pierwszej wersji, ale moim zdaniem nie należy to do kroku rozdzielczości.

</ 2 centy>

Lasse V. Karlsen
źródło
3
Rozumiem twój punkt widzenia i zgadzam się z tobą, jednak nadal potrzebuję instancji RepositoryA i RepositoryB, aby miały ten sam IDataContext, który musi być inny niż RepositoryC. Należy również zauważyć, że IRepositoryA i IRepositoryB mają właściwość dla IDataContext. Zaktualizuję nieco przykładowy kod.
NotDan
2
Świetna uwaga. Miałem właśnie dodać parametr string do konstruktora, ale po przejrzeniu tego punktu postanowiłem uczynić go pełnowymiarowym obiektem. Składa się tylko z napisu na tym punkcie, ale mogę już zobaczyć w jaki sposób mogę dodać więcej użytecznych właściwości do niego
Santosh Benjamin
9

Dzięki chłopaki… mój jest podobny do posta „Exist”. Zobacz poniżej:

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

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });
Kwex
źródło
5

Możesz użyć InjectionConstructor / InjectionProperty / InjectionMethod w zależności od architektury iniekcji w ResolvedParameter <T> („name”), aby uzyskać wystąpienie wstępnie zarejestrowanego obiektu w kontenerze.

W twoim przypadku ten obiekt musi być zarejestrowany z Nazwą i dla tego samego celu potrzebujesz ContainerControlledLifeTimeManager () jako 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
źródło
4
Czy jesteś pewien co do tego kodu? Nie kompiluje się ... Resolvepobiera kolekcję ResolverOverridei InjectionConstructornie jest plikiem ResolverOverride.
Thomas Levesque
Tak, źle to wygląda. Chociaż jedność powinna to zaprojektować w ten sposób. Jeśli nazwa parametru ulegnie zmianie, wszystko się psuje
Frank Q.
3

Bardzo krótka odpowiedź brzmi: nie. Unity obecnie nie ma możliwości przekazania do konstruktora parametrów, które nie są stałe ani wstrzyknięte, co udało mi się znaleźć. IMHO to największa rzecz, której brakuje, ale myślę, że wynika to z projektu, a nie z pominięcia.

Jak zauważa Jeff Fritz, teoretycznie można by stworzyć niestandardowego menedżera czasu życia, który wie, które wystąpienie kontekstu należy wstrzyknąć do różnych typów, ale jest to poziom sztywnego kodowania, który wydaje się przede wszystkim przeszkadzać w używaniu Unity lub DI.

Możesz cofnąć się o mały krok od pełnego DI i sprawić, by implementacje repozytorium były odpowiedzialne za tworzenie własnych kontekstów danych. Instancja kontekstu może być nadal rozpoznawana z kontenera, ale logika podejmowania decyzji, której z nich użyć, musiałaby przejść do implementacji repozytorium. Na pewno nie jest tak czysty, ale pozbyłby się problemu.

Neil Hewitt
źródło
1

Inną alternatywą, której możesz użyć (nie bardzo wiem, czy to dobra praktyka, czy nie), jest utworzenie dwóch kontenerów i zarejestrowanie instancji dla każdego z nich:

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

mam nadzieję, że to też pomoże

Samuel Carrijo
źródło
0

NotDan, myślę, że mogłeś odpowiedzieć na własne pytanie w komentarzach do lassevk.

Najpierw użyłbym LifetimeManager do zarządzania cyklem życia i liczbą wystąpień IDataContext, które tworzy Unity.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Wygląda na to, że ContainerControlledLifetimeManagerobiekt zapewnia zarządzanie instancjami, którego potrzebujesz. Po zastosowaniu tego LifetimeManager Unity powinien dodać to samo wystąpienie IDataContext do wszystkich obiektów, które wymagają zależności IDataContext.

Jeff Fritz
źródło