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);
RepositoryA
i RepositoryB
oba mają konstruktor, który przyjmuje IDataContext
parametr, i chcę, aby Unity zainicjował repozytorium z kontekstem, który mu przekazuję. Zwróć również uwagę, że IDataContext
nie jest zarejestrowany w Unity (nie chcę 3 wystąpień IDataContext
).
Foo(string name, int address) { ... }
container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
<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>
źródło
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") });
źródło
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")));
źródło
Resolve
pobiera kolekcjęResolverOverride
iInjectionConstructor
nie jest plikiemResolverOverride
.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.
źródło
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
źródło
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
ContainerControlledLifetimeManager
obiekt 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.źródło