Entity Framework: esiste già un DataReader aperto associato a questo comando

Sto usando Entity Framework e occasionalmente otterrò questo errore.

EntityCommandExecutionException {"There is already an open DataReader associated with this Command which must be closed first."} at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands... 

Anche se non sto facendo alcuna gestione manuale della connessione.

questo errore si verifica a intermittenza.

codice che triggers l’errore (abbreviato per facilità di lettura):

  if (critera.FromDate > x) { t= _tEntitites.T.Where(predicate).ToList(); } else { t= new List(_tEntitites.TA.Where(historicPredicate).ToList()); } 

utilizzando il modello Dispose per aprire ogni volta una nuova connessione.

 using (_tEntitites = new TEntities(GetEntityConnection())) { if (critera.FromDate > x) { t= _tEntitites.T.Where(predicate).ToList(); } else { t= new List(_tEntitites.TA.Where(historicPredicate).ToList()); } } 

ancora problematico

perché EF non può riutilizzare una connessione se è già aperta.

Non si tratta di chiudere la connessione. EF gestisce correttamente la connessione. La mia comprensione di questo problema è che ci sono più comandi di recupero dati eseguiti su una singola connessione (o un singolo comando con selezioni multiple) mentre il successivo DataReader viene eseguito prima che il primo abbia completato la lettura. L’unico modo per evitare l’eccezione è consentire più DataReaders nidificati = triggersre MultipleActiveResultSets. Un altro scenario quando questo accade sempre è quando si esegue iterazione del risultato della query (IQueryable) e si triggers il caricamento lazy per l’ quadro caricata all’interno dell’iterazione.

In alternativa all’utilizzo di MARS (MultipleActiveResultSets) è ansible scrivere il codice in modo da non aprire più set di risultati.

Quello che puoi fare è recuperare i dati nella memoria, in questo modo non avrai il lettore aperto. È spesso causato dall’iterazione di un set di risultati durante il tentativo di aprire un altro set di risultati.

Codice d’esempio:

 public class MyContext : DbContext { public DbSet Blogs { get; set; } public DbSet Posts { get; set; } } public class Blog { public int BlogID { get; set; } public virtual ICollection Posts { get; set; } } public class Post { public int PostID { get; set; } public virtual Blog Blog { get; set; } public string Text { get; set; } } 

Diciamo che stai facendo una ricerca nel tuo database che contiene questi:

 var context = new MyContext(); //here we have one resultset var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5); foreach (var blog in largeBlogs) //we use the result set here { //here we try to get another result set while we are still reading the above set. var postsWithImportantText = blog.Posts.Where(p=>p.Text.Contains("Important Text")); } 

Possiamo fare una semplice soluzione aggiungendo .ToList () in questo modo:

 var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList(); 

Ciò impone l’entityframework per caricare l’elenco in memoria, quindi quando lo iteriamo nel ciclo foreach non utilizza più il lettore di dati per aprire l’elenco, è invece in memoria.

Mi rendo conto che questo potrebbe non essere desiderato se si desidera eseguire il lazyload di alcune proprietà, ad esempio. Questo è per lo più un esempio che si spera spieghi come / perché potresti ottenere questo problema, in modo che tu possa prendere le decisioni di conseguenza

C’è un altro modo per superare questo problema. Se è un modo migliore dipende dalla tua situazione.

Il problema deriva dal caricamento lazy, quindi un modo per evitarlo non è avere un caricamento lazy, attraverso l’uso di Include:

 var results = myContext.Customers .Include(x => x.Orders) .Include(x => x.Addresses) .Include(x => x.PaymentMethods); 

Se si utilizzano gli appropriati Include s, è ansible evitare di abilitare MARS. Ma se ne manchi uno, otterrai l’errore, quindi abilitare MARS è probabilmente il modo più semplice per risolverlo.

Si ottiene questo errore, quando la raccolta che si sta tentando di ripetere è una specie di caricamento lento (IQueryble).

 foreach (var user in _dbContext.Users) { } 

La conversione della collezione IQueriable in un’altra raccolta enumerabile risolverà questo problema. esempio

 _dbContext.Users.ToList() 

Nota: .ToList () crea un nuovo set ogni volta e può causare problemi di prestazioni se si hanno a che fare con dati di grandi dimensioni.

Ho risolto il problema facilmente (pragmatico) aggiungendo l’opzione al costruttore. Quindi, lo uso solo quando necessario.

 public class Something : DbContext { public Something(bool MultipleActiveResultSets = false) { this.Database .Connection .ConnectionString = Shared.ConnectionString /* your connection string */ + (MultipleActiveResultSets ? ";MultipleActiveResultSets=true;" : ""); } ... 

prova nella stringa di connessione per impostare “MultipleActiveResultSets = true” che consente il multitasking sul database. “Server = yourserver; AttachDbFilename = database; ID utente = sa; Password = blah; MultipleActiveResultSets = true; App = EntityFramework” questo funziona per me … se la tua connessione in app.config o lo hai impostato a livello di codice … spero che questo utile

Inizialmente avevo deciso di utilizzare un campo statico nella mia class API per fare riferimento a un’istanza dell’object MyDataContext (dove MyDataContext è un object Contesto EF5), ma è quello che sembrava creare il problema. Ho aggiunto un codice simile al seguente ad ognuno dei miei metodi API e ho risolto il problema.

 using(MyDBContext db = new MyDBContext()) { //Do some linq queries } 

Come altre persone hanno affermato, gli oggetti Contesto dati EF non sono thread-safe. Quindi collocarli nell’object statico causerà l’errore “lettore di dati” nelle giuste condizioni.

La mia ipotesi originale era che la creazione di una sola istanza dell’object sarebbe stata più efficiente e offriva una migliore gestione della memoria. Da quello che ho raccolto ricercando questo problema, non è così. In effetti, sembra essere più efficiente trattare ogni chiamata alla tua API come un evento isolato e sicuro. Garantire che tutte le risorse siano correttamente rilasciate, in quanto l’object non rientra nell’ambito.

Questo ha senso soprattutto se si porta la tua API alla prossima progressione naturale che sarebbe quella di esporla come API WebService o REST.

Rivelazione

  • Sistema operativo: Windows Server 2012
  • .NET: Installato 4.5, Progetto che utilizza 4.0
  • Fonte dei dati: MySQL
  • Quadro dell’applicazione: MVC3
  • Autenticazione: moduli

Ho notato che questo errore si verifica quando invio un IQueryble alla vista e lo utilizzo in foreach doppio, dove foreach interno deve anche usare la connessione. Esempio semplice (ViewBag.parents può essere IQueryble o DbSet):

 foreach (var parent in ViewBag.parents) { foreach (var child in parent.childs) { } } 

La soluzione semplice è usare .ToList() sulla raccolta prima di usarla. Si noti inoltre che MARS non funziona con MySQL.

Una buona via di mezzo tra l’abilitazione di MARS e il recupero dell’intero set di risultati in memoria consiste nel recuperare solo ID in una query iniziale e quindi scorrere gli ID che materializzano ciascuna quadro man mano che si procede.

Ad esempio (utilizzando le quadro di esempio “Blog e post” come in questa risposta ):

 using (var context = new BlogContext()) { // Get the IDs of all the items to loop through. This is // materialized so that the data reader is closed by the // time we're looping through the list. var blogIds = context.Blogs.Select(blog => blog.Id).ToList(); // This query represents all our items in their full glory, // but, items are only materialized one at a time as we // loop through them. var blogs = blogIds.Select(id => context.Blogs.First(blog => blog.Id == id)); foreach (var blog in blogs) { this.DoSomethingWith(blog.Posts); context.SaveChanges(); } } 

Ciò significa che è sufficiente inserire poche migliaia di numeri interi in memoria, anziché migliaia di interi grafici di oggetti, che dovrebbero ridurre al minimo l’utilizzo della memoria consentendo al contempo di lavorare voce per voce senza abilitare MARS.

Un altro vantaggio di questo, come mostrato nell’esempio, è che è ansible salvare le modifiche mentre si passa attraverso ciascun elemento, invece di dover attendere fino alla fine del ciclo (o qualche altra soluzione simile), come sarebbe necessario anche con MARS abilitato (vedi qui e qui ).

Ho scoperto che avevo lo stesso errore e si è verificato quando stavo usando Func invece di Expression> per il tuo predicate .

Una volta che ho cambiato tutte le Func's Expression's l’eccezione ha smesso di essere lanciata.

Credo che EntityFramwork faccia qualcosa di intelligente con Expression's che semplicemente non fa con Func's

Se proviamo a raggruppare parte delle nostre condizioni in un metodo Func <> o estensione otterremo questo errore, supponiamo di avere un codice come questo:

 public static Func IsCurrent() { return p => (p.ValidFrom == null || p.ValidFrom < = DateTime.Now) && (p.ValidTo == null || p.ValidTo >= DateTime.Now); } Or public static IEnumerable IsCurrent(this IEnumerable prices) { .... } 

Questo getterà l’eccezione se proviamo ad usarlo in un Where (), quello che dovremmo fare invece è build un Predicate come questo:

 public static Expression> IsCurrent() { return p => (p.ValidFrom == null || p.ValidFrom < = DateTime.Now) && (p.ValidTo == null || p.ValidTo >= DateTime.Now); } 

Ulteriori ulteriori informazioni possono essere letti all’indirizzo: http://www.albahari.com/nutshell/predicatebuilder.aspx

Questo problema può essere risolto semplicemente convertendo i dati in una lista

  var details = _webcontext.products.ToList(); if (details != null) { Parallel.ForEach(details, x => { Products obj = new Products(); obj.slno = x.slno; obj.ProductName = x.ProductName; obj.Price = Convert.ToInt32(x.Price); li.Add(obj); }); return li; } 

Nella mia situazione il problema si è verificato a causa di una registrazione di iniezione di dipendenza. Stavo iniettando un servizio per ambito di richiesta che utilizzava dbcontext in un servizio registrato singleton. Quindi il dbcontext è stato utilizzato all’interno di più richieste e quindi l’errore.

Ho risolto questo problema utilizzando la seguente sezione di codice prima della seconda query:

  ...first query while (_dbContext.Connection.State != System.Data.ConnectionState.Closed) { System.Threading.Thread.Sleep(500); } ...second query 

puoi cambiare l’ora del sonno in millisecondi

PD Utile quando si usano i thread