Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

.NET Entity Framework et transactions

Création d'un Entity Framework global DbContext dans une application Web est très mauvais. Le DbContext la classe n'est pas thread-safe (et il en va de même pour ObjectContext d'Entity Framework v1 classer). Il est construit autour du concept d'unité de travail et cela signifie que vous l'utilisez pour opérer un seul cas d'utilisation :donc pour une transaction commerciale. Il est destiné à traiter une seule requête.

L'exception que vous obtenez se produit parce que pour chaque requête, vous créez une nouvelle transaction, mais essayez d'utiliser le même DbContext . Vous avez de la chance que le DbContext détecte cela et lève une exception, car vous avez maintenant découvert que cela ne fonctionnerait pas.

Le DbContext contient un cache local d'entités dans votre base de données. Il vous permet de faire un tas de modifications et enfin de soumettre ces modifications à la base de données. Lors de l'utilisation d'un seul DbContext statique , avec plusieurs utilisateurs appelant SaveChanges sur cet objet, comment est-il censé savoir exactement ce qui doit être commité et ce qui ne doit pas l'être ?

Parce qu'il ne sait pas, il sauvera tous modifications, mais à ce stade, une autre demande peut encore apporter des modifications. Lorsque vous avez de la chance, EF ou votre base de données échouera, car les entités sont dans un état non valide. Si vous n'avez pas de chance, les entités qui sont dans un état non valide sont enregistrées avec succès dans la base de données et vous découvrirez peut-être des semaines plus tard que vos données ont été corrompues.

La solution à votre problème est de créer au moins un DbContext par demande . Bien qu'en théorie, vous puissiez mettre en cache un contexte d'objet dans la session utilisateur, c'est également une mauvaise idée, car dans ce cas, le DbContext vivra généralement trop longtemps et contiendra des données obsolètes (car son cache interne ne sera pas automatiquement actualisé).

Notez également qu'avoir un DbContext par thread est à peu près aussi mauvais que d'avoir une seule instance pour l'application Web complète. ASP.NET utilise un pool de threads, ce qui signifie qu'un nombre limité de threads sera créé pendant la durée de vie d'une application Web. Cela signifie essentiellement que ces DbContext les instances vivront dans ce cas pendant toute la durée de vie de l'application, causant les mêmes problèmes d'obsolescence des données.

Vous pourriez penser qu'avoir un DbContext par thread est en fait thread-safe, mais ce n'est généralement pas le cas, car ASP.NET a un modèle asynchrone qui permet de terminer les requêtes sur un thread différent de celui où il a été démarré (et les dernières versions de MVC et de l'API Web autorisent même un nombre arbitraire de threads traitent une seule demande dans un ordre séquentiel). Cela signifie que le thread qui a lancé une requête et créé le ObjectContext peut devenir disponible pour traiter une autre demande bien avant la fin de cette demande initiale. Cependant, les objets utilisés dans cette requête (tels qu'une page Web, un contrôleur ou toute classe métier) peuvent toujours faire référence à ce DbContext . Étant donné que la nouvelle requête Web s'exécute dans ce même thread, elle obtiendra le même DbContext instance comme ce que l'ancienne requête utilise. Cela provoque à nouveau des conditions de concurrence dans votre application et provoque les mêmes problèmes de sécurité des threads que ce qu'un DbContext global cause de l'instance.