2013-08-27 16 views
9

Ho bisogno di aiuto con questo. Sto usando Unity come contenitore e voglio iniettare due diverse istanze dello stesso tipo nel mio costruttore.DI con Unity quando occorrono più istanze dello stesso tipo

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

.... e iQueue è implementato nella mia classe MessageQueue ....

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

Come posso iniettare due diverse istanze di MessageQueue nella mia classe di esempio? Ciascuna istanza di MessageQueue da creare con un percorso diverso.

risposta

3

è possibile registrare le due istanze con i nomi:

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

e allora si dovrebbe essere in grado di risolvere in base al nome, ma richiede l'utilizzo dell'attributo Dependency:

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

o iniettare la unità container e quindi risolvere le istanze all'interno del costruttore:

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

+1

Anche se questo risponde alla domanda, non è una soluzione molto buona, perché questo ingombra l'applicazione con gli attributi e accoppia l'applicazione al contenitore Unity. Preferirei usare invece un 'InjectionFactory'. – Steven

+1

Mi dispiace, ma il tuo secondo esempio (aggiornato) è una cattiva idea, ecco perché ottieni il mio downvote. Non dovresti promuovere l'iniezione del contenitore in classi, cioè [cattiva pratica] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). – Steven

+1

@Steven hai ragione, riprendo quella modifica. l'articolo collegato ha alcuni punti molto validi. –

1

Penso che questo sia stato chiesto prima su Stackoverflow. È necessario utilizzare ParameterOverride:

ParameterOverride consente di passare in valori per i parametri del costruttore per sovrascrivere un parametro passato a un determinato costruttore denominato. Solo il valore del parametro è sovrascritto, non il costruttore.

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

Grazie! Ma voglio risolvere Esempio come questo, container.Resolve (). Come posso specificare le due diverse istanze di MessageQueue? –

2

bene, non

è necessario utilizzare il modello di fabbrica in questo caso.

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

Si fa l'intenzione molto più chiara e si può internamente nel manico Example classe se le code sono non trovato o configurato in modo errato.

+0

Grazie! Questo è stato il mio primo pensiero, ma come evito di aggiornare le classi MessageQueue in fabbrica. Devo iniettare il contenitore in fabbrica e utilizzare ParameterOverrides in un'altra risposta? –

+0

Una factory è particolarmente utile per ritardare la creazione di istanze, ma nel tuo esempio stai ancora creando queste code durante la costruzione del grafo degli oggetti. Potremmo anche obiettare che hai complicato la progettazione di "Esempio", poiché ora dipende da un'astrazione extra. Vorrei rimuovere la dipendenza 'IQueueFactory' da' Example' e se è necessario l'uso della factory, registrarlo usando un 'InjectionFactory' come segue:' container.Register (new InjectionFactory (c => new Example (c.Resolve (). Create ("Receive"), c.Resolve Steven

+0

@ErikZ: Ok. quindi stai condividendo le code tra diverse classi? – jgauffin

3

Non tutto deve essere automaticamente collegato al container. È possibile registrare la classe Example in questo modo:

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

L'OP ha una sola classe 'MessageQueue' - nessuna separata' ReceiveQueue' e 'SendQueue'. –

+0

@ ErenErsönmez: Grazie per averlo portato alla mia attenzione; Ho perso questo punto. Questo rende la mia risposta molto più breve. Ho aggiornato la mia risposta. – Steven

+0

@Steven: la tua risposta originale è stata davvero interessante. Se la creazione di nuove classi e interfacce è un'opzione, penso che la soluzione sia buona. –

7

Ci sono molti modi per raggiungere i risultati desiderati (come evidenziato dalle risposte multiple). Ecco un altro modo con le registrazioni di nome (senza attributi):

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

Lo svantaggio di questo approccio è che se il costruttore Esempio è cambiato quindi il codice di registrazione deve essere modificato per corrispondere. Inoltre, l'errore sarebbe un errore di runtime e non un errore di compilazione più preferibile.

È possibile combinare quanto sopra con un'InjectionFactory per invocare il costruttore manualmente per dare tempo di compilazione controllo:

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

Se si utilizza una radice composizione allora l'uso delle corde magiche ("ReceiveQueue" e " SendQueue ") sarebbe limitato all'unica posizione di registrazione.

Problemi correlati