Ich verwende Entity Framework und gelegentlich erhalte ich diesen Fehler.
EntityCommandExecutionException
{"There is already an open DataReader associated with this Command which must be closed first."}
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands...
Auch wenn ich kein manuelles Verbindungsmanagement mache.
Dieser Fehler tritt zeitweise auf.
Code, der den Fehler auslöst (zur besseren Lesbarkeit verkürzt):
if (critera.FromDate > x) {
t= _tEntitites.T.Where(predicate).ToList();
}
else {
t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
}
Verwenden Sie Muster entsorgen, um jedes Mal eine neue Verbindung zu öffnen.
using (_tEntitites = new TEntities(GetEntityConnection())) {
if (critera.FromDate > x) {
t= _tEntitites.T.Where(predicate).ToList();
}
else {
t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
}
}
immer noch problematisch
Warum sollte EF eine Verbindung nicht wiederverwenden, wenn sie bereits geöffnet ist?
linq
entity-framework
sql-server-2008
Sonic Soul
quelle
quelle
predicate
und IhrehistoricPredicate
Variablen haben. Ich habe entdeckt , dass , wenn Sie passierenFunc<T, bool>
zuWhere()
wird es kompilieren und manchmal Arbeit (weil es funktioniert das „wo“ im Speicher). Was Sie tun sollten , istExpression<Func<T, bool>>
zu übergebenWhere()
.Antworten:
Es geht nicht darum, die Verbindung zu schließen. EF verwaltet die Verbindung korrekt. Mein Verständnis dieses Problems besteht darin, dass mehrere Datenabrufbefehle für eine einzelne Verbindung (oder einen einzelnen Befehl mit mehreren Auswahlen) ausgeführt werden, während der nächste DataReader ausgeführt wird, bevor der erste den Lesevorgang abgeschlossen hat. Die einzige Möglichkeit, die Ausnahme zu vermeiden, besteht darin, mehrere verschachtelte DataReaders zuzulassen = MultipleActiveResultSets zu aktivieren. Ein anderes Szenario, in dem dies immer der Fall ist, besteht darin, dass Sie das Ergebnis der Abfrage (IQueryable) durchlaufen und ein verzögertes Laden für die geladene Entität innerhalb der Iteration auslösen.
quelle
Alternativ zur Verwendung von MARS (MultipleActiveResultSets) können Sie Ihren Code so schreiben, dass Sie nicht mehrere Ergebnismengen öffnen.
Sie können die Daten in den Speicher abrufen, sodass der Reader nicht geöffnet ist. Dies wird häufig durch das Durchlaufen einer Ergebnismenge beim Versuch, eine andere Ergebnismenge zu öffnen, verursacht.
Beispielcode:
Nehmen wir an, Sie führen eine Suche in Ihrer Datenbank durch, die Folgendes enthält:
Wir können eine einfache Lösung dafür finden, indem wir .ToList () wie folgt hinzufügen :
Dies zwingt entityframework, die Liste in den Speicher zu laden. Wenn wir sie also in der foreach-Schleife durchlaufen, wird der Datenleser nicht mehr zum Öffnen der Liste verwendet, sondern im Speicher.
Mir ist klar, dass dies möglicherweise nicht erwünscht ist, wenn Sie beispielsweise einige Eigenschaften verzögern möchten. Dies ist meistens ein Beispiel, das hoffentlich erklärt, wie / warum Sie dieses Problem bekommen könnten, damit Sie Entscheidungen entsprechend treffen können
quelle
ToList
tausend Objekte bearbeiten, erhöht sich der Speicher um eine Tonne. In diesem speziellen Beispiel ist es besser, die innere Abfrage mit der ersten zu kombinieren, damit nur eine Abfrage generiert wird und nicht zwei.Es gibt einen anderen Weg, um dieses Problem zu überwinden. Ob es ein besserer Weg ist, hängt von Ihrer Situation ab.
Das Problem ergibt sich aus dem verzögerten Laden. Eine Möglichkeit, dies zu vermeiden, besteht darin, kein verzögertes Laden mithilfe von Include zu verwenden:
Wenn Sie die entsprechenden
Include
s verwenden, können Sie die Aktivierung von MARS vermeiden. Wenn Sie jedoch einen verpassen, wird der Fehler angezeigt. Daher ist die Aktivierung von MARS wahrscheinlich der einfachste Weg, ihn zu beheben.quelle
.Include
ist eine viel bessere Lösung als das Aktivieren von MARS und viel einfacher als das Schreiben eines eigenen SQL-Abfragecodes.Sie erhalten diesen Fehler, wenn die Sammlung, die Sie iterieren möchten, eine Art verzögertes Laden ist (IQueriable).
Das Konvertieren der IQueriable-Sammlung in eine andere aufzählbare Sammlung löst dieses Problem. Beispiel
Hinweis: .ToList () erstellt jedes Mal einen neuen Satz und kann zu Leistungsproblemen führen, wenn Sie mit großen Datenmengen arbeiten.
quelle
SELECT COUNT(*) FROM Users
= 5Ich habe das Problem leicht (pragmatisch) gelöst, indem ich dem Konstruktor die Option hinzugefügt habe. Daher benutze ich das nur bei Bedarf.
quelle
Versuchen Sie in Ihrer Verbindungszeichenfolge festzulegen
MultipleActiveResultSets=true
. Dies ermöglicht Multitasking in der Datenbank.Das funktioniert bei mir ... ob Ihre Verbindung in app.config oder Sie sie programmgesteuert einstellen ... hoffe dies ist hilfreich
quelle
Ich hatte ursprünglich beschlossen, ein statisches Feld in meiner API-Klasse zu verwenden, um auf eine Instanz des MyDataContext-Objekts zu verweisen (wobei MyDataContext ein EF5-Kontextobjekt ist), aber genau das schien das Problem zu verursachen. Ich habe jeder meiner API-Methoden Code wie den folgenden hinzugefügt, wodurch das Problem behoben wurde.
Wie andere Personen angegeben haben, sind die EF-Datenkontextobjekte NICHT threadsicher. Wenn Sie sie also in das statische Objekt einfügen, wird der Fehler "Datenleser" unter den richtigen Bedingungen auftreten.
Meine ursprüngliche Annahme war, dass das Erstellen nur einer Instanz des Objekts effizienter ist und eine bessere Speicherverwaltung ermöglicht. Nach dem, was ich zu diesem Thema gesammelt habe, ist dies nicht der Fall. Tatsächlich scheint es effizienter zu sein, jeden Aufruf Ihrer API als isoliertes, threadsicheres Ereignis zu behandeln. Stellen Sie sicher, dass alle Ressourcen ordnungsgemäß freigegeben werden, da das Objekt den Gültigkeitsbereich verlässt.
Dies ist insbesondere dann sinnvoll, wenn Sie Ihre API zum nächsten natürlichen Fortschritt führen, bei dem sie als WebService- oder REST-API verfügbar gemacht wird.
Offenlegung
quelle
Ich habe festgestellt, dass dieser Fehler auftritt, wenn ich ein IQueriable an die Ansicht sende und es in einem doppelten foreach verwende, wobei der innere foreach auch die Verbindung verwenden muss. Einfaches Beispiel (ViewBag.parents kann IQueriable oder DbSet sein):
Die einfache Lösung besteht darin,
.ToList()
die Sammlung vor ihrer Verwendung zu verwenden. Beachten Sie auch, dass MARS nicht mit MySQL funktioniert.quelle
ToList()
meinen ersten Anruf getätigt, um eine Sammlung von der DB zu erhalten. Dann habe ich eineforeach
Liste erstellt und die nachfolgenden Aufrufe haben einwandfrei funktioniert, anstatt den Fehler zu geben.Ich stellte fest, dass ich den gleichen Fehler hatte, und er trat auf, als ich einen
Func<TEntity, bool>
anstelle einesExpression<Func<TEntity, bool>>
für Ihren verwendetepredicate
.Einmal änderte ich dir alle
Func's
zuExpression's
die Ausnahme sie nicht mehr ausgelöst.Ich glaube, das
EntityFramwork
macht einige kluge Dinge, mitExpression's
denen es einfach nicht zu tun hatFunc's
quelle
(MyTParent model, Func<MyTChildren, bool> func)
berücksichtigt, damit meine ViewModels eine bestimmtewhere
Klausel für die generische DataContext-Methode angeben können . Nichts funktionierte, bis ich das tat.2 Lösungen zur Minderung dieses Problems:
.ToList()
Erzwingen Sie, dass das Speicher-Caching nach Ihrer Abfrage verzögert geladen wird , damit Sie es durchlaufen und einen neuen DataReader öffnen können..Include
(/ zusätzliche Entitäten, die Sie in die Abfrage laden möchten /) Dies wird als eifriges Laden bezeichnet, mit dem Sie (tatsächlich) zugeordnete Objekte (Entitäten) während der Ausführung einer Abfrage mit dem DataReader einbeziehen können.quelle
Ein guter Mittelweg zwischen dem Aktivieren von MARS und dem Abrufen der gesamten Ergebnismenge im Speicher besteht darin, nur IDs in einer anfänglichen Abfrage abzurufen und dann die IDs zu durchlaufen, die jede Entität materialisieren, während Sie fortfahren.
Zum Beispiel (unter Verwendung der Beispielentitäten "Blog and Posts" wie in dieser Antwort ):
Dies bedeutet, dass Sie nur einige tausend Ganzzahlen in den Speicher ziehen, im Gegensatz zu Tausenden ganzer Objektdiagramme. Dies sollte die Speichernutzung minimieren und es Ihnen ermöglichen, Element für Element zu arbeiten, ohne MARS zu aktivieren.
Ein weiterer netter Vorteil davon ist, wie Sie im Beispiel sehen können, dass Sie Änderungen speichern können, während Sie die einzelnen Elemente durchlaufen, anstatt bis zum Ende der Schleife (oder einer anderen solchen Problemumgehung) warten zu müssen, wie dies selbst bei erforderlich wäre MARS aktiviert (siehe hier und hier ).
quelle
context.SaveChanges();
innere Schleife :(. Dies ist nicht gut. Es muss außerhalb der Schleife sein.In meinem Fall stellte ich fest, dass vor den Aufrufen von myContext.SaveChangesAsync () Anweisungen zum Warten fehlten. Das Hinzufügen von Warten auf diese asynchronen Aufrufe hat die Probleme mit dem Datenleser für mich behoben.
quelle
Wenn wir versuchen, einen Teil unserer Bedingungen in eine Func <> - oder Erweiterungsmethode zu gruppieren, wird dieser Fehler angezeigt. Angenommen, wir haben einen Code wie den folgenden:
Dies löst die Ausnahme aus, wenn wir versuchen, sie in einem Where () zu verwenden. Stattdessen sollten wir ein Prädikat wie das folgende erstellen:
Weitere Informationen finden Sie unter: http://www.albahari.com/nutshell/predicatebuilder.aspx
quelle
Dieses Problem kann einfach durch Konvertieren der Daten in eine Liste gelöst werden
quelle
In meiner Situation trat das Problem aufgrund einer Registrierung der Abhängigkeitsinjektion auf. Ich habe einen Dienst pro Anforderungsbereich, der einen Datenbankkontext verwendet, in einen registrierten Singleton-Dienst eingefügt. Daher wurde der Datenbankkontext innerhalb einer Mehrfachanforderung und damit des Fehlers verwendet.
quelle
In meinem Fall hatte das Problem nichts mit der MARS-Verbindungszeichenfolge zu tun, sondern mit der JSON-Serialisierung. Nach dem Upgrade meines Projekts von NetCore2 auf 3 habe ich diesen Fehler erhalten.
Weitere Informationen finden Sie hier
quelle
Ich habe dieses Problem mithilfe des folgenden Codeabschnitts vor der zweiten Abfrage gelöst:
Sie können die Schlafzeit in Millisekunden ändern
PD Nützlich bei der Verwendung von Threads
quelle