2012-09-10 5 views
17

Attualmente sto usando il seguente codice:Cosa succede ad usare economico quando mi trasferisco in Dependency Injection

public class MyProvider 
{ 
    public MyProvider() 
    { 
    } 

    public void Fetch() 
    { 
     using (PopClient popClient = new PopClient()) 
     { 
      .... 
     } 
    } 
} 

Perché voglio essere in grado di unit test del metodo Fetch e per il fatto che posso' t mock PopClient, ho creato un'interfaccia e una classe wrapper che chiama in PopClient. Il mio codice aggiornato si presenta come:

public class MyProvider 
{ 
    private readonly IPopClient popClient; 

    public MyProvider(IPopClient popClient) 
    { 
     this.popClient = popClient; 
    } 

    public void Fetch() 
    { 
     using (var pop3 = popClient) 
     { 
      .... 
     } 
    } 
} 

Sto usando Ninject per l'iniezione di dipendenza e io non sono del tutto sicuro che tipo di effetto l'istruzione using avrà nel codice aggiornato dal Ninject già creata un'istanza popclient e iniettò nel costruttore.

L'istruzione using eliminerà l'oggetto pop3 e lascerà l'oggetto popClient da solo in modo che Ninject possa gestirlo o l'istruzione using interferirà con Ninject?

Qual è l'approccio corretto in questo caso? Qualsiasi intuizione sarebbe molto utile.

+0

Se si dispone di popClient, cosa succede all'oggetto MyProvider?! Non dovrebbe essere disposto anche ?! Perché è rimasto un solo membro readonly disposto nella classe. Se è così, penso che sia meglio mettere un metodo di dispose per la classe MyProvider. –

risposta

14

La variabile pop3 sarà dato lo stesso riferimento a un IPopClient oggetto che popClient ha, quindi quando l'istruzione using è finita, l'oggetto a cui entrambe le variabili locali e istanza sarà Dispose() d, probabilmente ponendolo in uno stato incoerente per ulteriore utilizzo.

Se si desidera utilizzare più istanze di IPopClient, uno per Fetch() chiamata, ciò che si dovrebbe fare è iniettare un "metodo factory":

public class MyProvider 
{ 
    private readonly Func<IPopClient> createPopClient; 

    public MyProvider(Func<IPopClient> popClientFactory) 
    { 
     this.createPopClient = popClientFactory; 
    } 

    public void Fetch() 
    { 
     using (var pop3 = createPopClient()) 
     { 
      .... 
     } 
    } 
} 

Ora, quando si chiama Fetch(), si eseguirà la fabbrica metodo che restituirà un nuovo riferimento a un IPopClient, che può essere utilizzato e quindi eliminato senza influire su altre chiamate a tale metodo.

AutoFac supporta l'iniezione di metodi di fabbrica per tipi registrati senza alcuna configurazione aggiuntiva (da cui il nome, credo); Credo che quando si configura un container Ninject sia necessario registrare esplicitamente un "getter" come metodo factory per un determinato tipo restituito (che può essere semplice come un lambda ()=>new PopClient() o può usare una chiamata al metodo di risoluzione del contenitore).

+1

migliore per creare un'interfaccia di fabbrica. L'intento è più chiaro. +1 in entrambi i casi per il modello di fabbrica – jgauffin

+0

Finché si ha 'Ninject.Extensions.Factory.dll' nella directory AppDomain.Base, Func viene generato automaticamente - si veda [il wiki di' Ninject.Extensions.Factory'] (https: // github.com/ninject/ninject.extensions.factory/wiki/Func) (cioè non c'è bisogno di registrare nulla) –

+0

In secondo luogo il commento fatto da @jgauffin sopra. Un'interfaccia di fabbrica ha un po 'più di lavoro, ma a lungo andare ripagherà. L'intento sarà più chiaro e anche il deridere è più facile. – Pflugs

1

Quando si imposta le associazioni, dichiarare il campo di applicazione:

https://github.com/ninject/ninject/wiki/Object-Scopes

Ninject chiamerà smaltire sugli oggetti che ne derivano per voi, quindi assicuratevi di scrivere i vostri metodi di smaltimento in tutti gli oggetti che date a Ninject da gestire.

+0

Il problema è che con esso impostato come è attualmente, MyProvider ottiene sempre un'istanza di un oggetto IPopClient da usare; ciò significa che se il codice effettua più chiamate a Fetch() utilizzando un'istanza di MyProvider, questo fallirà indipendentemente dall'ambito registrato di IPopClient. – KeithS

+0

Sì, il metodo di recupero verrà chiamato più volte, quindi nel codice corrente (senza DI) mi è sempre stata garantita una nuova istanza pop a causa dell'istruzione using. Nel codice DI credo che l'ambito transitorio creerà solo un'istanza che verrà utilizzata da più chiamate e l'istruzione using eliminerà quell'istanza dopo che è stata effettuata la prima chiamata. – Thomas

+0

@Thomas - che è esattamente corretto - vedi la mia risposta.In breve, se vuoi mantenere l'istruzione using, non vuoi iniettare una singola istanza, ma piuttosto un metodo factory che puoi chiamare per produrre tutte le istanze che vuoi. – KeithS

Problemi correlati