Erstellen eines globalen Entity Framework DbContext
in einer Webanwendung ist sehr schlecht. Der DbContext
-Klasse ist nicht Thread-sicher (dasselbe gilt für den ObjectContext
von Entity Framework v1 Klasse). Es basiert auf dem Konzept der Arbeitseinheit
und das heißt, Sie bedienen damit einen einzigen Anwendungsfall:also für einen Geschäftsvorfall. Es soll eine einzige Anfrage bearbeiten.
Die Ausnahme, die Sie erhalten, tritt auf, weil Sie für jede Anfrage eine neue Transaktion erstellen, aber versuchen, denselben DbContext
zu verwenden . Sie haben Glück, dass der DbContext
erkennt dies und löst eine Ausnahme aus, da Sie jetzt herausgefunden haben, dass dies nicht funktioniert.
Der DbContext
enthält einen lokalen Cache von Entitäten in Ihrer Datenbank. Es ermöglicht Ihnen, eine Reihe von Änderungen vorzunehmen und diese Änderungen schließlich an die Datenbank zu übermitteln. Bei Verwendung eines einzelnen statischen DbContext
, wobei mehrere Benutzer SaveChanges
aufrufen Woher soll es bei diesem Objekt wissen, was genau übergeben werden soll und was nicht?
Da es das nicht weiß, speichert es alle Änderungen, aber zu diesem Zeitpunkt nimmt möglicherweise noch eine andere Anfrage Änderungen vor. Wenn Sie Glück haben, schlägt entweder EF oder Ihre Datenbank fehl, da sich die Entitäten in einem ungültigen Zustand befinden. Wenn Sie Pech haben, werden Entitäten, die sich in einem ungültigen Zustand befinden, erfolgreich in der Datenbank gespeichert und Sie könnten Wochen später feststellen, dass Ihre Daten beschädigt wurden.
Die Lösung für Ihr Problem besteht darin, mindestens einen DbContext
pro Anfrage
. Während Sie theoretisch einen Objektkontext in der Benutzersitzung zwischenspeichern könnten, ist dies ebenfalls keine gute Idee, da in diesem Fall der DbContext
wird normalerweise zu lange leben und veraltete Daten enthalten (weil sein interner Cache nicht automatisch aktualisiert wird).
Beachten Sie auch, dass es einen DbContext
gibt pro Thread ist ungefähr so schlecht wie eine einzige Instanz für die gesamte Webanwendung. ASP.NET verwendet einen Thread-Pool, was bedeutet, dass während der Lebensdauer einer Webanwendung eine begrenzte Anzahl von Threads erstellt wird. Das bedeutet im Grunde, dass diese DbContext
Instanzen leben in diesem Fall noch für die Lebensdauer der Anwendung und verursachen die gleichen Probleme mit veralteten Daten.
Sie könnten denken, dass Sie einen DbContext
haben pro Thread ist tatsächlich Thread-sicher, aber das ist normalerweise nicht der Fall, da ASP.NET ein asynchrones Modell hat, das es ermöglicht, Anfragen auf einem anderen Thread zu beenden als dort, wo es gestartet wurde (und die neuesten Versionen von MVC und Web-API erlauben sogar eine beliebig viele Threads bearbeiten eine einzelne Anfrage in sequentieller Reihenfolge). Das bedeutet, dass der Thread, der eine Anfrage gestartet und den ObjectContext
erstellt hat können für die Bearbeitung einer weiteren Anfrage verfügbar werden, lange bevor diese ursprüngliche Anfrage abgeschlossen ist. Die in dieser Anforderung verwendeten Objekte (z. B. eine Webseite, ein Controller oder eine beliebige Geschäftsklasse) können jedoch immer noch auf diesen DbContext
verweisen . Da die neue Webanforderung in demselben Thread ausgeführt wird, erhält sie denselben DbContext
Instanz als das, was die alte Anfrage verwendet. Dies führt wiederum zu Race-Bedingungen in Ihrer Anwendung und zu denselben Thread-Sicherheitsproblemen wie ein globaler DbContext
Instanz verursacht.