Il thread DbContext è sicuro?

Mi chiedevo se la class DbContext è thread-safe, presumo che non lo sia, poiché attualmente sto eseguendo thread DbContext che accedono a DbContext nella mia applicazione e sto ottenendo un host di eccezioni di blocco e altre cose che sembrano essere thread relazionato.

Fino a poco tempo fa non DbContext errori … ma fino a poco tempo fa non stavo accedendo a DbContext nei thread.

Se ho ragione, cosa suggerirebbe la gente come soluzione?

Non è thread-safe. Crea semplicemente una nuova istanza di DbContext nel tuo thread.

No non è thread-safe: l’EF intero non è thread-safe perché il contesto EF non dovrebbe mai essere condiviso.

Modificato – vecchia risposta di seguito.

Ora uso sempre questo modello con DbContext:

 using(var db = new LogDbContext()) { // Perform work then get rid of the thing } 

Il mio approccio di uno per thread di richiesta significava che gli oggetti memorizzati nella cache di DbContext rimanevano invariati e diventavano obsoleti anche mentre altre istanze di DbContext scrivevano nuovi valori nel database effettivo dietro di esso. Ciò creerebbe alcuni strani problemi, ad esempio una richiesta di esecuzione di un inserimento e la successiva richiesta per l’elenco in arrivo su un thread diverso che aveva un elenco memorizzato nella cache dei dati per quella query.

Esistono approcci che rendono il lavoro sotto riportato e addirittura migliorano le prestazioni delle app in stile molte letture / in poche scritture, ma richiedono più design e strategia rispetto al modello molto più semplice di cui sopra.

Aggiornare

Uso anche un utile metodo di supporto per i metodi di libreria, come le chiamate di registrazione. Ecco il metodo di supporto:

  public static async Task Using(Db db, Func action) { if (db == null) { using (db = new Db()) { await action(db); } } else { await action(db); } } 

Con questo posso facilmente scrivere codice che prende un DbContext esistente opzionale o crea un’istanza all’interno di un contesto usando, a seconda di come viene chiamato.

Ad esempio, mentre si lavora con un DbContext, potrei caricare alcuni dati, registrare alcune informazioni e quindi salvare i dati – è meglio fare tutto ciò con lo stesso DbContext dal punto di vista delle prestazioni. D’altra parte potrei anche voler registrare qualcosa in risposta a una semplice azione, e né caricare né scrivere altri dati. Sfruttando il metodo sopra, posso avere solo un metodo di registrazione che funziona se vuoi lavorare all’interno di un DbContext esistente o no:

 public async Task WriteLine(string line, Db _db = null) { await Db.Using(_db, db => { db.LogLines.Add(new LogLine(line)); await db.SaveChangesAsync(); }); } 

Ora questa chiamata al metodo può essere chiamata all’interno o all’esterno di un DbContext esistente e si comporta ancora nel modo giusto, invece di dover avere 2 versioni di questo e di ogni altro metodo di registrazione della convenienza o altro metodo di utilità che ho, e invece di dover conoscere e pianificare il contesto di ogni chiamata che verrà mai effettuata a loro o ai loro chiamanti. Questo in sostanza mi restituisce uno dei vantaggi della strategia threadstatic qui sotto, in cui non dovevo preoccuparmi di quando esattamente il db si apriva nelle chiamate di utility che dovrebbero essere preoccupate al riguardo.

Vecchia risposta

Di solito gestisco la sicurezza dei thread con EF DbContext in questo modo:

 public class LogDbContext : DbContext { . . . [ThreadStatic] protected static LogDbContext current; public static LogDbContext Current() { if (current == null) current = new LogDbContext(); return current; } . . . } 

Con questo in atto posso ottenere un DbContext per questo thread in questo modo:

 var db = LogDbContext.Current(); 

È importante notare che dal momento che ogni DbContext conserva la propria cache locale, ogni thread avrà ora una propria cache separata di oggetti entity framework, che può introdurre un comportamento pazzo se non si è pronti per questo. Tuttavia, la creazione di nuovi oggetti DbContext può essere costosa e questo approccio riduce al minimo tale costo.