Entity Framework, DBContext et en utilisant() + asynchrone?
Il y a une chose qui m'énerve depuis longtemps à propos de l'Entity Framework.
L'année dernière, j'ai écrit une grosse application pour un client à l'aide de EF. Et pendant le développement de tout fonctionnait très bien.
Nous avons expédié le système en août. Mais après quelques semaines, j'ai commencé à voir bizarre fuites de mémoire sur la production-serveur. Mon ASP.NET MVC 4 processus a été la prise de toutes les ressources de la machine après une couple de jours de course (8 GO). Ce n'était pas bon. J'ai chercher partout sur le net et vu que vous devriez vous entourer de toutes vos EF les requêtes et les opérations dans un using()
bloc de sorte que le contexte peut être éliminé.
Dans un jour, j'ai refait tout mon code pour utiliser using()
et cela a résolu mon problème, depuis lors, le processus repose sur une constante de l'utilisation de la mémoire.
La raison pour laquelle je n'ai pas l'entourer de mes requêtes, en premier lieu, cependant, c'est que j'ai commencé mes premiers contrôleurs et les dépôts à partir de Microsoft propres échafaudages inclus dans Visual Studio, ils n'ont pas l'entourent de requêtes à l'aide, au lieu de cela, il avait le DbContext
comme une variable d'instance du contrôleur lui-même.
Tout d'abord: si c'est vraiment important de disposer du contexte (quelque chose qui ne serait pas étrange, le dbconnection
doit être fermé, et ainsi de suite), Microsoft devrait peut-être avoir ce dans tous leurs exemples!
Maintenant, j'ai commencé à travailler sur un nouveau gros projet avec toutes mes connaissances acquises à l'arrière de ma tête et j'ai été essayer les nouvelles fonctionnalités de .NET 4.5 et EF 6 async
et await
. EF 6.0 a toutes ces méthodes asynchrones (e.g SaveChangesAsync
, ToListAsync
, et ainsi de suite).
public Task<tblLanguage> Post(tblLanguage language)
{
using (var langRepo = new TblLanguageRepository(new Entities()))
{
return langRepo.Add(RequestOrganizationTypeEnum, language);
}
}
Dans la classe TblLanguageRepo
:
public async Task<tblLanguage> Add(OrganizationTypeEnum requestOrganizationTypeEnum, tblLanguage language)
{
...
await Context.SaveChangesAsync();
return langaugeDb;
}
Cependant, quand je suis présent tout autour de mes déclarations dans un using()
bloc-je obtenir de l'exception, DbContext was disposed
, avant que la requête a été en mesure de revenir. Ce comportement est normal. L'exécution de la requête asynchrone et le using
bloc est terminé devant de la requête. Mais comment dois-je disposer de mes contexte d'une manière appropriée lors de l'utilisation du async et await fonctions de ef 6??
Merci de me pointer dans la bonne direction.
Est using()
nécessaires en EF 6? Pourquoi Microsoft exemples ne jamais fonctionnalité? Comment utilisez-vous async fonctionnalités et de les éliminer de votre contexte correctement?
- Cette question est étroitement liée: EF Contexte de Données - Async/Await & Multithreading
Vous devez vous connecter pour publier un commentaire.
Votre code:
est de disposer du référentiel avant de retourner un
Task
. Si vous faites le codeasync
:puis il va disposer le référentiel juste avant la
Task
complète. Ce qui se passe lorsque vous appuyez sur laawait
, la méthode renvoie un incomplètesTask
(à noter que lausing
bloc est toujours "actif" à ce point). Puis, quand lelangRepo.Add
fin de la tâche, laPost
méthode reprend l'exécution et dispose de lalangRepo
. Lorsque lePost
méthode est terminée, le retour de l'Task
est terminé.Pour plus d'informations, voir mon
async
intro.Add
awaitable? J'ai pensé qu'il revient tout juste d'un TEntity (en supposant que langRepo est un DbSet)...Add
dans ce cas est donnée dans le cas des op question. Il est awaitable (et faut vraiment être appeléAddAsync
).Je pencherais pour le "one DbContext par demande' la voie, et la réutilisation de la DbContext dans la demande. Comme toutes les tâches doivent être achevés à la fin de la requête de toute façon, vous pouvez éliminer en toute sécurité à nouveau.
Voir c'est à dire: Un DbContext par demande ASP.NET MVC (sans conteneur IOC)
Certains autres avantages:
les requêtes précédentes, en sauver quelques-unes des requêtes supplémentaires.
using
états encombrer votre code.Si vous êtes à l'aide de n-niveaux de programmation des modèles, votre contrôleur ne doit jamais savoir qu'une demande de base de données est en train d'être fait. Tout cela devrait se produire dans votre couche de service.
Il ya un couple de façons de le faire. L'une consiste à créer 2 constructeurs pour la classe, celui qui crée un contexte et un qui accepte un déjà existant contexte. De cette façon, vous pouvez passer le contexte autour si vous êtes déjà dans la couche de service, ou en créer un nouveau si c'est le contrôleur/modèle de l'appel de la couche de service.
L'autre est de créer un interne de surcharge de chaque méthode et accepter la situation.
Mais, oui, vous devriez être en l'enveloppant dans une aide.
En théorie, la collecte des ordures DOIT les nettoyer sans les emballer, mais je n'ai pas entièrement confiance à la GC.
Je d'accord avec @Dirk Boer que la meilleure façon de gérer DbContext durée de vie est avec un conteneur IoC, qui dispose du contexte lors de la requête http est terminée. Toutefois, si ce n'est pas une option, vous pouvez aussi faire quelque chose comme ceci:
La
using
déclaration est tout sucre syntaxique pour se débarrasser d'un objet à la fin d'un bloc de code. Vous pouvez obtenir le même effet sansusing
bloc en appelant simplement.Dispose
vous-même.Venez pour penser à elle, vous ne devriez pas obtenir un objet disposé des exceptions si vous utilisez le
await
mot-clé dans l'utilisation de bloc:Si vous voulez garder votre méthode synchrone, mais que vous souhaitez enregistrer dans la base de données de manière asynchrone, ne pas utiliser l'instruction à l'aide. Comme @danludwig dit, c'est juste un sucre syntaxique. Vous pouvez appeler la SaveChangesAsync() la méthode et puis jeter le contexte une fois la tâche accomplie. Une façon de le faire est: est-ce
Prendre note que le lambda de passer ContinueWith() sera également exécutée de manière asynchrone.
À mon humble avis, c'est de nouveau un problème causé par l'utilisation de lazy-loading. Après avoir disposé de votre contexte, vous ne pouvez pas lazy-load a bien plus à cause de l'élimination du contexte ferme la connexion sous-jacente au serveur de base de données.
Si vous avez de chargement différé activé et que l'exception se produit après la
using
portée, alors s'il vous plaît voir https://stackoverflow.com/a/21406579/870604