2013-08-27 13 views
9

Potrzebuję pomocy w tym zakresie. Używam Unity jako mojego kontenera i chcę wstrzyknąć dwa różne instancje tego samego typu do mojego konstruktora.DI z Unity, gdy potrzebnych jest wiele instancji tego samego typu.

class Example 
{ 
    Example(IQueue receiveQueue, IQueue sendQueue) {} 
} 

.... i IQueue realizowany jest w mojej klasie kolejka komunikatów ....

class MessageQueue : IQueue 
{ 
    MessageQueue(string path) {} 
} 

Jak można wstrzykiwać dwa różne instancje kolejka komunikatów na moim przykładzie klasy? Każda z instancji MessageQueue do utworzenia z inną ścieżką.

Odpowiedz

3

Można zarejestrować dwa przypadki z nazwami:

myContainer.RegisterInstance<IQueue>("ReceiveQueue", myReceiveMessageQueue); 
myContainer.RegisterInstance<IQueue>("SendQueue", mySendMessageQueue); 

a następnie powinny być w stanie rozwiązać po imieniu, ale wymaga przy użyciu atrybutu Dependency:

class Example 
{ 
    Example([Dependency("ReceiveQueue")] IQueue receiveQueue, 
      [Dependency("SendQueue")] IQueue sendQueue) { 
    } 
} 

lub wstrzyknąć pojemnik jedności, a następnie rozwiąż instancje w konstruktorze:

class Example 
{ 
    Example(IUnityContainter container) 
    { 
     _receiveQueue = container.Resolve<IQueue>("ReceiveQueue"); 
     _sendQueue = container.Resolve<IQueue>("SendQueue"); 
    } 
} 

+1

Chociaż to odpowiada na pytanie, nie jest to bardzo dobre rozwiązanie, ponieważ powoduje to zaśmiecanie aplikacji atrybutami i powiązanie aplikacji z kontenerem Unity. Zamiast tego wolałbym użyć 'InjectionFactory'. – Steven

+1

Przykro mi, ale twój drugi (zaktualizowany) przykład jest złym pomysłem, dlatego otrzymujesz moje poparcie. Nie należy promować wstrzykiwania pojemnika na lekcje, to jest [zła praktyka] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). – Steven

+1

@Steven masz rację, odbieram tę zmianę. połączony artykuł ma kilka bardzo ważnych punktów. –

1

Myślę, że zostało to wcześniej zadane na Stackoverflow. Należy użyć parametru ParameterOverride:

ParameterOverride umożliwia przekazywanie wartości parametrów konstruktora w celu nadpisania parametru przekazanego do określonego konstruktora nazwanego. Tylko wartość parametru jest nadpisywana, a nie konstruktor.

Link to MSDN Article

Link to Stackoverflow Article

var exampleInstance = new Example(); 

var queue1 = unityContainer.Resolve<IQueue>(new ParameterOverrides<MessageQueue> { { "path", "yourPath" }}); 

var queue2 = unityContainer.Resolve<IQueue>(new ParameterOverrides<MessageQueue> { { "path", "yourPath2Queue2" }}); 

exampleInstance.Example(queue1,queue2); 
+0

Dziękujemy! Ale chcę rozwiązać ten przykład, container.Resolve (). Jak określić dwie różne instancje MessageQueue? –

2

Cóż, nie

należy użyć wzoru fabryka w tej sprawie.

class Example 
{ 
    Example(IQueueFactory factory) 
    { 
     _sendQueue = factory.Create("MySend"); 
     _receiveQueue = factory.Create("MyReceive"); 
    } 
} 

To sprawia, że ​​zamiar dużo bardziej wyraźny i można wewnętrznie w uchwycie Example klasy jeśli kolejki są nie znaleziono lub nieprawidłowo skonfigurowane.

+0

Dziękujemy! To była moja pierwsza myśl, ale jak mogę uniknąć tworzenia klas MessageQueue w fabryce. Czy powinienem wstrzyknąć pojemnik do fabryki i użyć ParameterOverrides zaproponowanego w innej odpowiedzi? –

+0

Fabryka jest szczególnie przydatna do opóźniania tworzenia instancji, ale w twoim przykładzie wciąż tworzysz te kolejki podczas tworzenia wykresu obiektów. Możemy nawet twierdzić, że skomplikowałeś projekt "Przykładu", ponieważ teraz zależy on od dodatkowej abstrakcji. Chciałbym usunąć zależność 'IQueueFactory' od' Example' i jeśli potrzebne jest użycie fabryki, zarejestruj ją używając 'InjectionFactory' w następujący sposób:' container.Register (new InjectionFactory (c => nowy przykład (c.Resolve) () Utwórz ("Odbierz"), c.Resolve Steven

+0

@ErikZ: Ok. więc dzielisz kolejki między różnymi klasami? – jgauffin

3

Nie wszystko musi być automatycznie łączone przez pojemnik. Można zarejestrować klasę Example takiego:

container.Register<Example>(new InjectionFactory(c => 
{ 
    var receive = new MessageQueue("receivePath"); 
    var send = new MessageQueue("sendPath"); 
    return new Example(receive, send); 
}); 
+0

OP ma jedną klasę 'MessageQueue' - brak oddzielnych klas' ReceiveQueue' i 'SendQueue'. –

+0

@ ErenErsönmez: Dziękuję za zwrócenie mi tej uwagi; Tęskniłem za tym punktem. To sprawia, że ​​moja odpowiedź jest znacznie krótsza. Zaktualizowałem swoją odpowiedź. – Steven

+0

@Steven: Twoja oryginalna odpowiedź była naprawdę interesująca. Jeśli tworzenie nowych klas i interfejsów jest opcją, myślę, że to rozwiązanie wygląda dobrze. –

7

Istnieje wiele sposobów, aby osiągnąć pożądane wyniki (o czym świadczy wielu odpowiedzi). Oto inny sposób za pomocą nazwanych rejestracje (bez atrybutów):

IUnityContainer container = new UnityContainer(); 

container.RegisterType<IQueue, MessageQueue>("ReceiveQueue", 
    new InjectionConstructor("receivePath")); 

container.RegisterType<IQueue, MessageQueue>("SendQueue", 
    new InjectionConstructor("sendPath")); 

container.RegisterType<Example>(
    new InjectionConstructor(
     new ResolvedParameter<IQueue>("ReceiveQueue"), 
     new ResolvedParameter<IQueue>("SendQueue"))); 

Example example = container.Resolve<Example>(); 

Wadą tego podejścia jest to, że jeśli Przykład konstruktor zostanie zmieniona następnie kod rejestracyjny musi także zostać zmodyfikowane, aby dopasować. Ponadto błąd byłby błędem środowiska wykonawczego, a nie bardziej korzystnym błędem czasu kompilacji.

Można połączyć powyższe z InjectionFactory powołania konstruktora ręcznie dać kompilacji kontroli czasu:

IUnityContainer container = new UnityContainer(); 

container.RegisterType<IQueue, MessageQueue>("ReceiveQueue", 
    new InjectionConstructor("receivePath")); 

container.RegisterType<IQueue, MessageQueue>("SendQueue", 
    new InjectionConstructor("sendPath")); 

container.RegisterType<Example>(new InjectionFactory(c => 
    new Example(c.Resolve<IQueue>("ReceiveQueue"), 
       c.Resolve<IQueue>("SendQueue")))); 

Example example = container.Resolve<Example>(); 

Jeśli używasz korzeń skład następnie korzystanie z magicznych ciągów („ReceiveQueue” i " SendQueue ") byłby ograniczony do jednej lokalizacji rejestracji.

Powiązane problemy