Entity Framework, DBContext et en utilisant () + async?

il y a une chose qui me dérange depuis longtemps à propos du cadre de L'entité.

L'année dernière, j'ai écrit une grosse demande pour un client en utilisant EF. Et pendant le développement, tout a bien fonctionné.

Nous avons expédié le système en août. Mais après quelques semaines, j'ai commencé à voir des fuites de mémoire bizarres sur le serveur de production. Mon ASP.NET le processus MVC 4 absorbait toutes les ressources de la machine après quelques jours de fonctionnement (8 Go). Ce n'était pas bon. J'ai cherché sur le net et j'ai vu que vous devriez entourer toutes vos requêtes et opérations EF dans un bloc using() pour que le contexte puisse être éliminé.

en un jour j'ai refactorisé tout mon code pour utiliser using() et cela a résolu mes problèmes, depuis lors le processus repose sur une utilisation régulière de la mémoire.

la raison pour laquelle je n'ai pas entouré mes requêtes en premier lieu cependant est que j'ai commencé mes premiers contrôleurs et dépôts de Microsofts propres échafaudages inclus dans Visual Studio, ceux-ci n'ont pas entouré ses requêtes d'utilisation, à la place il avait le DbContext comme une variable d'instance du contrôleur lui-même.

tout D'abord : S'il est vraiment important de disposer du contexte (quelque chose qui ne serait pas bizarre, le dbconnection doit être fermé et ainsi de suite), Microsoft devrait peut-être avoir ceci dans tous leurs exemples!

maintenant, j'ai commencé travailler sur un nouveau grand projet avec tous mes apprentissages à l'arrière de ma tête et j'ai essayé les nouvelles fonctionnalités de .NET 4.5 et EF 6 async et await . L'EF 6.0 utilise toutes ces méthodes async(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 j'entoure maintenant mes déclarations dans un using() bloc je reçois l'exception, DbContext was disposed , avant que la requête a été capable de revenir. C'est un comportement attendu. La requête tourne async et le bloc using est terminé avant la requête. Mais comment disposer correctement de mon contexte en utilisant les fonctions async et attente de ef 6??

s'il vous plaît, dirigez-moi dans la bonne direction.

est-ce que using() est nécessaire dans EF 6? Pourquoi Microsoft exemples ne jamais fonctionnalité? Comment utilisez-vous les fonctionnalités async et disposez-vous de votre contexte correctement?

23
demandé sur Scott Weldon 2014-02-07 19:36:24

5 réponses

votre code:

public Task<tblLanguage> Post(tblLanguage language)
{
    using (var langRepo = new TblLanguageRepository(new Entities()))
    {
        return langRepo.Add(RequestOrganizationTypeEnum, language);
    }
}

dispose du dépôt avant de retourner un Task . Si vous faites le code async :

public async Task<tblLanguage> Post(tblLanguage language)
{
    using (var langRepo = new TblLanguageRepository(new Entities()))
    {
        return await langRepo.Add(RequestOrganizationTypeEnum, language);
    }
}

Il dispose alors du dépôt juste avant que le Task ne soit terminé. Ce qui se passe en fait , c'est que lorsque vous appuyez sur le await , la méthode retourne un Task incomplet (notez que le bloc using est toujours "actif" à ce point). Puis, lorsque la tâche langRepo.Add complète, la méthode Post reprend l'exécution et dispose du langRepo . Lorsque la méthode Post est terminée, le formulaire Task retourné est rempli.

pour plus d'information, voir mon async intro .

24
répondu Stephen Cleary 2014-02-07 16:23:00

j'opterais pour le mode 'one DbContext per request', et réutilise le DbContext dans la requête. Comme toutes les tâches doivent être effectuées à la fin de la demande de toute façon, vous pouvez disposer à nouveau en toute sécurité.

voir: un DbContext par requête en ASP.NET MVC (sans conteneur CIO)

quelques autres avantages:

  • certaines entités peuvent déjà être matérialisées dans le DbContext de requêtes précédentes, enregistrement de quelques requêtes supplémentaires.
  • vous n'avez pas toutes ces déclarations supplémentaires using qui encombrent votre code.
4
répondu Dirk Boer 2017-05-23 11:55:10

si vous utilisez des patteurs de programmation À n niveaux appropriés, votre contrôleur ne devrait même pas savoir qu'une demande de base de données est faite. Cela devrait se produire dans votre couche de service.

il y a plusieurs façons de le faire. L'un est de créer 2 constructeurs par classe, un qui crée un contexte et un qui accepte un contexte déjà existant. De cette façon, vous pouvez passer le contexte si vous êtes déjà dans la couche service, ou en créer une nouvelle si c'est la contrôleur / model appelant la couche service.

L'autre est de créer un interne de surcharge de chaque méthode et d'accepter le contexte.

Mais, oui, vous devriez les emballer dans une utilisation.

en théorie, la collecte des ordures devrait nettoyer ces derniers sans les emballer, mais je ne fais pas entièrement confiance au GC.

1
répondu Scottie 2014-02-07 15:49:00

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:

var dbContext = new MyDbContext();
var results = await dbContext.Set<MyEntity>.ToArrayAsync();
dbContext.Dispose();

la déclaration using n'est que du sucre syntaxique pour disposer d'un objet à la fin d'un bloc de code. Vous pouvez obtenir le même effet sans bloc using en j'appelle .Dispose vous-même.

en y repensant, vous ne devriez pas avoir d'exception si vous utilisez le mot-clé await dans le bloc d'utilisation:

public async Task<tblLanguage> Post(tblLanguage language)
{
    using (var langRepo = new TblLanguageRepository(new Entities()))
    {
        var returnValue = langRepo.Add(RequestOrganizationTypeEnum, language);
        await langRepo.SaveChangesAsync();
        return returnValue;
    }
}
1
répondu danludwig 2017-05-23 11:47:32

IMHO, c'est encore un problème causé par l'usage du chargement paresseux. Après avoir disposé de votre contexte, vous ne pouvez plus charger paresseusement une propriété car la disposition du contexte ferme la connexion sous-jacente au serveur de base de données.

si vous avez activé la charge paresseuse et que l'exception se produit après la portée using , alors s'il vous plaît voir https://stackoverflow.com/a/21406579/870604

0
répondu ken2k 2017-05-23 10:31:37