2009-10-02 11 views
12

Sto usando Prism, che è anche il bel contenitore Unity IoC. Sono nuovo del concetto, quindi non mi sono ancora messo le mani intorno. Quello che voglio fare ora è creare un oggetto usando il contenitore IoC, ma passando anche un parametro extra. Mi permetta di spiegare con un esempio ..:Creazione di oggetti usando Unity Resolve con parametri aggiuntivi

Ho una classe che accetta un oggetto comandi. Questo è registrato nel contenitore CIO, ti gestirlo bene:

public class Person 
{ 
    public Person(IApplicationCommands commands) { .. } 
    .. 
} 

Person person = _container.Resolve<Person>(); 

ora - voglio passare a un altro argomento - per esempio il nome della persona. Tuttavia, voglio ancora utilizzare il contenitore IoC per gestire la risoluzione e quindi ottenere gli altri paramenter dal contenitore IoC. Ma inserisci il nome come parametro "personalizzato". Può essere fatto?

public class Person 
{ 
    public Person(IApplicationCommands commands, string name) { .. } 
    .. 
} 

string name = "John"; 
Person person = _container.Resolve<Person>(name); // ....?? 

Questo esempio non sembra funzionare, ma esiste un modo per farlo funzionare? Oppure il contenitore Unity IoC richiede che tutti i parametri siano registrati nel contenitore prima di chiamare Resolve?

risposta

9

Modifica: questa risposta è obsoleta, a mio parere, perché ha un'ipotesi di una versione precedente di Unity. NotDan's answer è meglio.


Hai alcune opzioni. Sono onestamente un po 'zoppi, ma funzioneranno.

Opzione 1: con mirino Container

Se si desidera utilizzare costruttore iniezione, è necessario creare un scope contenitore e mettere i dati in che con scope container:

IUnityContainer subContainer = _container.CreateChildContainer(); 

//Don't do this... create a custom type other than string, like 
// MyConstructorParams or something like that... this gets the point across. 
subContainer.RegisterInstance<string>("John"); 
Person person = subContainer.Resolve<Person>(); 

Opzione 2: Metodo di inizializzazione

Quello che di solito faccio, però, è avere un metodo di inizializzazione separato sulle mie oggetti di destinazione per le variabili di istanza:

public class Person 
{ 
    public Person(IApplicationCommands commands) 
    { .. } 
    public void Initialize(string name) { .. } 

    .. 
} 

E poi che l'uso diventa:

Person person = container.Resolve<Person>(); 
person.Initialize("John"); 

Né è particolarmente piacevole, ma farà il suo lavoro. L'importante è scegliere una convenzione e attenervisi, altrimenti ti sentirai un po 'perso.

Spero che questo aiuti.

+0

io non lo consiglio uno di questi approcci. Anche se l'utilizzo di contenitori con ambito o la registrazione di istanze con nome logico sono tecniche valide per alcuni scenari, non sono appropriati dato il presunto contesto di creazione di nuove entità DDD. Certamente non è possibile prevedere quali nuovi oggetti Person potrebbero aver bisogno di essere creati se questo è legato alla registrazione di nuovi clienti, e anche se tu potessi ... beh, penso che tu capisca il problema che si presenterebbe. La seconda opzione è sicuramente più valida, ma è meglio esprimere le dipendenze richieste tramite i parametri del costruttore ... –

+0

Anche questo non affronta quello che vedo essere un difetto nella modellazione di Persona per iniziare e l'uso diretto del contenitore come localizzatore di servizi per la creazione di un tipo di questo tipo. –

+0

Certamente l'opzione del contenitore con scope è il più sciocco delle due opzioni, ma funziona. Ho anche dato per scontato che l'OP abbia minimizzato la sua domanda e non stia cercando di fare qualcosa di simile su una semplice Entità, ma su un oggetto di servizio più complesso. Ho sempre questo problema con questi oggetti dove l'oggetto ha alcune dipendenze, ma ha anche bisogno di dati di istanza passati ad esso che devo intervenire quando viene passato. Questo è il motivo per cui altri contenitori IoC, come Ninject, supportano esattamente ciò che l'OP vuole. L'unità non lo è ancora. –

0

Non riesco a pensare a un modo per farlo con l'iniezione del costruttore. Penso che dovrai usare property injection per le dipendenze (contrassegnato con l'attributo Dipendenza) e quindi prendere solo la stringa nel costruttore, creare un'istanza, quindi usare BuildUp per collegare le dipendenze o rendere la stringa una proprietà anche tu imposta manualmente dopo la risoluzione.

4

Ci sono alcune scelte che potrebbe prendere in considerazione:

Nel caso in cui è necessario creare una nuova entità che ha legittimi dipendenze, oltre a tutti i dati che vengono forniti (ad esempio, il nome del cliente), incapsulare questo in una fabbrica che è stato anch'esso iniettato nell'oggetto chiamante:

Person person = _personFactory.CreatePerson("bubba"); 

la fabbrica può essere iniettato con dipendenze dell'entità e forniti al costruttore se richiesto o impostato in altro modo se facoltativo:

0.123.
var person = new Person("bubba", _domainService); 

Per le dipendenze transitori variabili, come ad esempio una strategia utilizzata da un particolare metodo, utilizzare Doppio spedizione:

public class Person 
{ 
    public void DoSomethingWith(SomeStrategy strategy) 
    { 
     strategy.DoSomething(this); 
    } 
} 

+0

Questo è vero solo se si presume che Person sia solo un'entità semplice, che è valida in questo caso, ma non affronta questa lacuna in Unity per tutti i casi.Ninject e alcuni altri contenitori IoC hanno la capacità di fare esattamente ciò che l'OP vuole, ma Unity no, lasciandoci alcune opzioni piuttosto limitate. Sono d'accordo che il contenitore dell'ambito è la cosa più brutta di sempre (non lo faccio personalmente), ma è una soluzione. –

+0

La persona è solo una classe di finzione per semplificare questa domanda. Nel mio scenario non lo faccio per una classe di dominio, ma una rappresentazione ViewModel della classe di dominio. Sto usando Prism, e la vista "Persona" in questo caso dovrà attivare i comandi per mostrare alcuni sotto-dati in alcune regioni, e per questo ho bisogno di IApplicationCommands. – stiank81

+0

@Anderson Imes: non ero a conoscenza di alcuni contenitori abilitati. Posso immaginare alcuni casi in cui ciò potrebbe essere utile, ma nel complesso raccomando ancora un approccio di fabbrica o doppio dispatch sull'uso di un container come servizio di localizzazione, poiché questi approcci preservano l'ignoranza dell'infrastruttura. Per lo meno, è possibile limitare la dipendenza del codice da invocazioni dirette al contenitore incapsulando la tecnica specifica del contenitore in fabbrica. –

17

Can I pass constructor parameters to Unity's Resolve() method?

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });" 
+2

Ehi! Molto bella! Non l'avevo visto ma è simile a quello che ha aggiunto Ninject di recente. Dovrò tirare giù l'ultima versione, sembra. Grazie! –

+1

Cosa succede se cambi il nome del parametro nel costruttore? –

+3

Sto indovinando no workie .. – NotDan

Problemi correlati