Posso passare i parametri del costruttore al metodo Resolve () di Unity?


91

Sto usando Unity di Microsoft per l'inserimento delle dipendenze e voglio fare qualcosa del genere:

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

RepositoryAed RepositoryBentrambi hanno un costruttore che accetta un IDataContextparametro e desidero che Unity inizializzi il repository con il contesto in cui lo passo. Si noti inoltre che IDataContextnon è registrato con Unity (non voglio 3 istanze di IDataContext).

Risposte:


71

Ad oggi hanno aggiunto questa funzionalità:

È nell'ultimo drop qui:

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

Discussione su di esso qui:

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

Esempio:

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



2
"La classe 'Microsoft.Practices.Unity.ParameterOverrides' non dispone di parametri di tipo". Sto usando Unity 3.5; questo codice è valido solo per una versione precedente di Unity?
Thomas Levesque

Per me funziona. Nota: la tua classe deve avere un costruttore parametrizzato con parametro "nome" e parametro "indirizzo". Foo(string name, int address) { ... }
fino

Utilizzando Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis

38

<2 centesimi>

E se in seguito decidessi di utilizzare un servizio diverso che richiede più o meno del solo contesto?

Il problema con i parametri del costruttore e IoC è che i parametri sono in definitiva legati al tipo di calcestruzzo utilizzato, anziché essere parte del contratto definito dall'interfaccia del servizio.

Il mio suggerimento sarebbe che tu risolva anche il contesto e credo che Unity dovrebbe avere un modo per evitare di costruirne 3 istanze, oppure dovresti considerare un servizio di fabbrica che ha un modo per te di costruire l'oggetto.

Ad esempio, cosa succede se in seguito decidi di costruire un repository che non si basa affatto su un database tradizionale, ma utilizza invece un file XML per produrre dati fittizi per il test? Come faresti a fornire il contenuto XML a quel costruttore?

IoC si basa sul disaccoppiamento del codice, legando il tipo e la semantica degli argomenti ai tipi concreti, non hai davvero eseguito correttamente il disaccoppiamento, c'è ancora una dipendenza.

"Questo codice può parlare con qualsiasi tipo di repository possibilmente, fintanto che implementa questa interfaccia ... Oh, e utilizza un contesto dati".

Ora, so che altri contenitori IoC supportano questo, e lo avevo anche nella mia prima versione, ma a mio parere non appartiene alla fase di risoluzione.

</ 2 centesimi>


3
Capisco il tuo punto e sono d'accordo con te, tuttavia ho ancora bisogno che le istanze di RepositoryA e RepositoryB abbiano lo stesso IDataContext, che deve essere diverso da RepositoryC. Si noti inoltre che IRepositoryA e IRepositoryB hanno una proprietà per IDataContext. Aggiornerò un po 'il codice di esempio.
NotDan

2
Ottimo punto. Stavo per aggiungere un parametro stringa al costruttore, ma dopo aver visto questo punto, ho deciso di renderlo un oggetto in piena regola. Consiste solo della stringa a questo punto, ma posso già vedere come potrei aggiungere più proprietà utili ad esso
Santosh Benjamin

9

Grazie ragazzi ... il mio è simile al post di "Exist". Vedi sotto:

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

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });

5

È possibile utilizzare InjectionConstructor / InjectionProperty / InjectionMethod a seconda dell'architettura di iniezione all'interno di ResolvedParameter <T> ("nome") per ottenere un'istanza di un oggetto preregistrato nel contenitore.

Nel tuo caso, questo oggetto deve essere registrato con un nome e per la stessa somma hai bisogno di ContainerControlledLifeTimeManager () come 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")));

4
Sei sicuro di questo codice? Non si compila ... Resolverichiede una raccolta di ResolverOverridee InjectionConstructornon è un file ResolverOverride.
Thomas Levesque

Sì, sembra sbagliato. Sebbene l'unità avrebbe dovuto progettarlo in quel modo. Se il nome del parametro cambia, tutto si interrompe
Frank Q.

3

La risposta molto breve è: no. Unity attualmente non ha modo di passare parametri nel costruttore che non sono costanti o iniettati, che sono stato in grado di trovare. IMHO questa è l'unica cosa più grande che manca, ma penso che sia per progettazione piuttosto che per omissione.

Come nota Jeff Fritz, potresti in teoria creare un Lifetime Manager personalizzato che sappia quale istanza di contesto iniettare in vari tipi, ma questo è un livello di hard-coding che sembra ovviare allo scopo di utilizzare Unity o DI in primo luogo.

Potresti fare un piccolo passo indietro rispetto al DI completo e rendere le implementazioni del tuo repository responsabili della creazione dei propri contesti dei dati. L' istanza del contesto può ancora essere risolta dal contenitore ma la logica per decidere quale utilizzare dovrebbe entrare nell'implementazione del repository. Non è così puro, certo, ma eliminerebbe il problema.


1

Un'altra alternativa che potresti usare (non so davvero se sia una buona pratica o meno) è creare due contenitori e registrare un'istanza per ciascuno:

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

spero che anche questo aiuti


0

NotDan, penso che potresti aver risposto alla tua stessa domanda nei commenti a lassevk.

Innanzitutto, utilizzerei un LifetimeManager per gestire il ciclo di vita e il numero di istanze di IDataContext che Unity crea.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Sembra che l' ContainerControlledLifetimeManageroggetto ti fornirà la gestione delle istanze di cui hai bisogno. Con quel LifetimeManager in atto, Unity dovrebbe aggiungere la stessa istanza di IDataContext a tutti gli oggetti che richiedono una dipendenza IDataContext.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.