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.
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
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
@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