2015-10-12 26 views
6

sto usando semplice iniettore 3.0.4In semplice iniettore perché è un errore per un singolo o di servizio con ambito di dipendere da un servizio transitorio

Ho un servizio che, con uno stile di vita di ambito dipende da un servizio che ha uno stile di vita transitorio.

Quando chiamo container.Verify() viene visualizzato un errore di diagnostica relativo alla mancata corrispondenza dello stile di vita.

Il servizio temporaneo che causa i problemi viene iniettato in altri servizi transitori quindi prima di andare avanti e rendere il mio intero progetto ambito ho bisogno di chiedere. Perché una dipendenza da un ambito di vita di qualsiasi stile di vita a transitoria è un problema? I transienti vengono aggiornati freschi ogni volta che vengono iniettati, quindi nient'altro può interferire con esso. In sostanza, la vita di un oggetto transitorio è governata dal servizio in cui viene iniettata.

Inoltre ho già letto la documentazione su questo argomento da here e capisco perché non si desidera un singleton dipendente da un servizio con scope, ad esempio, ma sicuramente una dipendenza da transienti è sempre sicura?

risposta

10

I transienti vengono aggiornati freschi ogni volta che vengono iniettati, quindi nient'altro può interferire con esso.

transitori sono newed ogni volta che li richiede dal contenitore, ma una volta che vengono iniettate in un componente, saranno restare per tutto il tempo che vive componenti. Quindi se il componente consumante è un singleton, significa che trascinerà tutte le sue dipendenze, rendendole praticamente anche singleton. Questo comportamento diventa evidente quando si guarda a come si dovrebbe in genere implementare l'iniezione di dipendenza:

public class SomeComponent 
{ 
    private readonly ILogger logger; 
    private readonly IService service; 

    public SomeComponent(ILogger logger, IService service) { 
     this.logger = logger; 
     this.service = service; 
    } 
} 

Come si può vedere, le dipendenze sono memorizzati in campi privati ​​del componente, causerà loro di rimanere in vita il più a lungo SomeComponent lives e SomeComponent continueranno a utilizzare le stesse dipendenze.

In sostanza, la durata di un oggetto transitorio è regolata dal servizio a cui è stato iniettato.

Esatto; lo stile di vita di un componente sarà almeno lungo quanto il suo consumatore. Tuttavia, una dipendenza può avere più consumatori con stili di vita diversi, rendendo davvero difficile vedere quanto tempo vivrà la dipendenza. Quando iniettato nel consumatore 1 potrebbe vivere per la durata della richiesta, mentre un'altra istanza di tale dipendenza, immessa nel consumatore 2, vivrà fino a quando l'applicazione lo fa.

Proprio come le circostanze con ambito, le registrazioni transitori non sono generalmente thread-safe; altrimenti li avresti registrati come singleton. Mantenere i transienti in vita per un periodo di tempo più lungo, può ovviamente causare bug o bug di concorrenza relativi a dati obsoleti. Questo è il motivo per cui Simple Injector lo disabilita di default e genera un'eccezione.

Potrebbe essere confuso da come Autofac definisce i suoi stili di vita, rispetto a Simple Injector. Autofac non contiene uno stile di vita transitorio. Invece ha uno stile di vita InstancePerDependency. Tecnicamente è lo stesso transitorio, ma l'intento è molto diverso. Con InstancePerDependency si dice: "Questo componente è destinato a vivere finché il suo consumatore, qualunque sia lo stile di vita che potrebbe essere". Ci possono essere casi in cui ciò ha senso, ma in questo modo si sta effettivamente ignorando l'elefante nella stanza e ho visto la mancanza di rilevamento di essere una fonte comune di bug.Nella maggior parte dei casi, se non ti interessa lo stile di vita dei componenti, significa che dovrebbe essere registrato come singleton - non InstancePerDependency.

Il motivo per cui il semplice iniettore non consente ai transienti di essere iniettati in istanze con scope è perché le istanze con scope possono anche vivere a lungo (a seconda dell'applicazione) e non si può sempre presumere che i transitori possano essere tranquillamente iniettato in consumatori con scope.

Alla fine si tratta di comunicare l'intento del codice. Nel caso in cui un componente sia stateless o thread-safe, dovresti registrarlo come singleton. Non è thread-safe, lo si registra come ambito o transitorio. Ciò rende chiaro a chiunque legge la configurazione come dovrebbe gestire tale componente E consente a Simple Injector di rilevare eventuali errori di configurazione.

Mentre Simple Injector rileva errori di configurazione, sono giunto alla conclusione che la configurazione DI può essere notevolmente semplificata quando il sistema è progettato attorno a grafici di oggetti costituiti esclusivamente da componenti singleton. Ho espresso questi pensieri here. Ciò rimuoverà molte delle complessità che affrontiamo quando si lavora con l'iniezione delle dipendenze, e persino espone la violazione dei principi SOLID ancora più rapidamente di quanto già DA non sia già in grado di fare.

prima di andare avanti e fare tutto il mio progetto con scope

Questo è qualcosa che non avrei consigli di fare. Solitamente si vede che solo alcuni dei "componenti foglia" nell'applicazione sono ambiti (come ad esempio DbContext). I componenti dell'ambito non dipendono da molti altri componenti. I componenti che scrivi tu stesso dovrebbero in genere essere privi di stato e non richiedono alcuna memorizzazione nella cache. Quindi, nel caso in cui rendere l'oggetto singleton dei grafici sia (non ancora) un'opzione, di solito farei la maggior parte del transitorio del grafico dell'oggetto, e solo quelli con pochi componenti fogliati. Poiché i transienti possono tranquillamente dipendere da istanze con scope, tutto funzionerà come previsto.

+1

Questa è una spiegazione esaustiva e penso di capire: è tutta una questione di sicurezza dei thread. Il mio progetto è organizzato in modo che solo i singleton vengano utilizzati da più thread. Scoped e transients non sono condivisi tra thread. Transient è il nostro default con 1 o 2 singleton e una manciata di scope. Mentre il messaggio di errore è frustrante in questo momento, posso vedere come potrebbe aiutarmi a schivare un proiettile se avesse scoperto un caso in cui uno dei miei singleton dipendeva da un transitorio. – Twisted

+0

Non capisco. Posso avere una classe thread-safe come dipendenza 'transitoria', iniettata in singleton e altre classi transitorie. Lo "Stile di vita" non ha necessariamente bisogno di correlare con la sicurezza del thread. (Singleton <-> thread-safe, Transient <-> non thread-safe) – Efrain

+0

@Efrain, il punto è, quel servizio temporaneo. Non sarà più transitorio quando iniettato in un singleton. A volte questo potrebbe funzionare, ma questo spesso dà problemi e dato che questa è una fonte di bug, Simple Injector ti impedisce di farlo. Se il tuo servizio è destinato a vivere una lunga vita: renderlo singleton. – Steven