Een globaal Entity Framework maken DbContext
in een webapplicatie is erg slecht. De DbContext
class is niet thread-safe (en hetzelfde geldt voor Entity Framework v1's ObjectContext
klas). Het is opgebouwd rond het concept van de werkeenheid
en dit betekent dat je het gebruikt om een single use case te gebruiken:dus voor een zakelijke transactie. Het is bedoeld om één enkel verzoek af te handelen.
De uitzondering die u krijgt, gebeurt omdat u voor elk verzoek een nieuwe transactie maakt, maar probeert dezelfde DbContext
te gebruiken . Je hebt geluk dat de DbContext
detecteert dit en genereert een uitzondering, omdat je er nu achter bent gekomen dat dit niet werkt.
De DbContext
bevat een lokale cache van entiteiten in uw database. Hiermee kunt u een aantal wijzigingen aanbrengen en deze wijzigingen uiteindelijk indienen bij de database. Bij gebruik van een enkele statische DbContext
, met meerdere gebruikers die SaveChanges
aanroepen over dat object, hoe moet het weten wat er precies moet worden gepleegd en wat niet?
Omdat het het niet weet, zal het alles opslaan wijzigingen, maar op dat moment kan een ander verzoek nog wijzigingen aanbrengen. Als je geluk hebt, zal EF of je database falen, omdat de entiteiten zich in een ongeldige staat bevinden. Als u pech heeft, worden entiteiten met een ongeldige status met succes in de database opgeslagen en kunt u er weken later achter komen dat uw gegevens beschadigd zijn geraakt.
De oplossing voor uw probleem is om ten minste één DbContext
per verzoek
. Hoewel je in theorie een objectcontext in de gebruikerssessie zou kunnen cachen, is dit ook een slecht idee, omdat in dat geval de DbContext
duurt meestal te lang en bevat verouderde gegevens (omdat de interne cache niet automatisch wordt vernieuwd).
Merk ook op dat het hebben van één DbContext
per thread is ongeveer net zo slecht als het hebben van één enkele instantie voor de volledige webtoepassing. ASP.NET maakt gebruik van een threadpool, wat inhoudt dat er tijdens de levensduur van een webapplicatie een beperkt aantal threads wordt aangemaakt. Dit betekent in feite dat die DbContext
instanties zullen in dat geval nog steeds in leven blijven voor de levensduur van de applicatie, wat dezelfde problemen veroorzaakt met de verouderde gegevens.
Je zou kunnen denken dat het hebben van een DbContext
per thread is eigenlijk thread-safe, maar dit is meestal niet het geval, aangezien ASP.NET een asynchroon model heeft dat het mogelijk maakt verzoeken af te werken op een andere thread dan waar het is gestart (en de nieuwste versies van MVC en Web API zelfs toestaan willekeurig aantal threads behandelen één enkel verzoek in sequentiële volgorde). Dit betekent dat de thread die een verzoek heeft gestart en de ObjectContext
. heeft gemaakt kunnen beschikbaar komen om een ander verzoek te verwerken lang voordat dat eerste verzoek is voltooid. De objecten die in dat verzoek worden gebruikt (zoals een webpagina, controller of een business class), kunnen echter nog steeds verwijzen naar die DbContext
. Aangezien het nieuwe webverzoek in dezelfde thread wordt uitgevoerd, krijgt het dezelfde DbContext
instantie als wat het oude verzoek gebruikt. Dit veroorzaakt opnieuw race-omstandigheden in uw toepassing en veroorzaakt dezelfde thread-veiligheidsproblemen als wat een globale DbContext
instantie veroorzaakt.