Nous utilisons le cadre d'entité pour la communication avec la base de données dans nos méthodes de service WCF. Nous avons récemment exécuté l'outil de révision de code sur notre code de service. Comme d'habitude, nous avons reçu de nombreuses propositions d'examen par outil et de nombreux commentaires suggéraient de disposer de l'objet contextuel Entity Framework. Ma question est donc la suivante: si j'utilise un objet de contexte Entity Framework dans une méthode et qu'une fois que je sors de cette méthode, GC ne nettoie pas l'objet de contexte? Avons-nous besoin de disposer explicitement d'un objet de contexte?
Simplement: DbContext
implements IDisposable
, vous devez donc vous en débarrasser, manuellement, dès que vous avez terminé.
Vous n'avez pas besoin de vous en débarrasser, car le GC le collectera éventuellement, mais le GC n'est pas déterministe: vous ne savez jamais quand "finalement" le sera. Jusqu'à ce qu'il soit éliminé, il contiendra des ressources qui ne sont pas utilisées - par exemple, il peut toujours avoir une connexion à une base de données ouverte. Ces ressources ne sont pas libérées avant que le CPG ne s'exécute, sauf si vous en disposez manuellement. En fonction de détails spécifiques, vous pouvez constater que vous avez bloqué inutilement les ressources réseau, les accès aux fichiers et que vous aurez certainement plus de mémoire réservée que nécessaire.
Il existe également un autre problème potentiel: lorsque vous supprimez un objet manuellement, le GC n'a généralement pas besoin d'appeler le finaliseur sur cet objet (le cas échéant). Si vous laissez le CPG se débarrasser automatiquement d'un objet avec un finaliseur, il placera l'objet dans une file d'attente du finaliseur et promouvra automatiquement l'objet à la génération suivante du CPG. Cela signifie qu’un objet doté d’un finaliseur s’accroche toujours pendant des ordres de grandeur plus longs que nécessaire avant d’être GCed (les générations GC successives étant collectées moins fréquemment). DBContext
tomberait probablement dans cette catégorie car la connexion à la base de données sous-jacente sera du code non géré.
(Utile référence .)
Je pense que la meilleure approche est de le coder dans une déclaration using
using(var cx = new DbContext())
{
//your stuff here
}
de sorte qu'il a automaitaclly disposé
En général, si quelque chose implémente IDisposable
, c’est une bonne idée de la supprimer explicitement lorsque vous avez terminé. Cela est particulièrement vrai si vous ne possédez pas l'implémentation dudit objet; vous devriez le traiter comme une boîte noire dans ce cas. De plus, même s'il n'était pas nécessairement "nécessaire" de s'en départir maintenant, cela pourrait l'être à l'avenir.
Par conséquent, à mon humble avis, la question de savoir si vous "devez" disposer explicitement de l'objet n'est pas pertinente. Si elle demande à être éliminée - en vertu de la mise en œuvre de IDisposable
-, elle doit être éliminée.
La chose recommandée à faire avec un contexte DBC est de ne pas le supprimer du tout (c'est l'exception à la règle dans la plupart des cas), même s'il s'agit d'un objet jetable.
Un exemple du problème, le premier exemple prend un appel et l’évalue dans la déclaration using, tandis que le second l’évalue après. (le premier s'exécute, le second génère l'erreur The operation cannot be completed because the DbContext has been disposed.
)
List<Test> listT;
using (Model1 db = new Model1())
{
listT = db.Tests.ToList(); //ToList Evaluates
}
foreach (var a in listT)
{
Console.WriteLine(a.value);
}
IEnumerable<Test> listT1;
using (Model1 db = new Model1())
{
listT1 = db.Tests;
}
foreach (var a in listT1) //foreach evaluates (but at wrong time)
{
Console.WriteLine(a.value);
}
Le même problème se produit dans
IEnumerable<Test> listT1;
Model1 db = new Model1();
listT1 = db.Tests;
db.Dispose();
foreach (var a in listT1) //foreach evaluates (but at wrong time)
{
Console.WriteLine(a.value);
}
Aussi longtemps que vous n'ouvrez pas la connexion manuellement, vous êtes en sécurité en utilisant simplement
IEnumerable<Test> listT1;
Model1 db = new Model1();
listT1 = db.Tests;
foreach (var a in listT1) //foreach evaluates (but at wrong time)
{
Console.WriteLine(a.value);
}
et ne jetant jamais. comme il prendra soin de lui-même dans la plupart des circonstances, comme c'est ce qu'il est conçu pour faire.
Maintenant, si vous ouvrez de force une connexion, le contexte ne la fermera pas automatiquement une fois le transfert terminé, car il ne sait pas quand et alors vous devez/devez disposer de l'objet ou fermer la connexion et conserver l'objet non divulgué.
Lecture minuit supplémentaire:
Il n'est pas nécessaire de disposer explicitement de DbContext.
Ceci est un ancien blocage des outils pré-DbContext. DbContext est un code géré et maintient de manière optimiste les connexions à la base de données. Pourquoi sledgehammer? Quoi de pressé? Pourquoi ne pas laisser le ramasse-miettes choisir le meilleur moment pour nettoyer lorsque la machine est inactive ou a besoin de mémoire? Reportez-vous également à cet article: https://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext/
En ne vous souciant pas de devoir vous débarrasser, cela simplifiera et optimisera votre code. En règle générale, je pourrais hériter d'une classe "helper" de base de données pour utiliser un getter afin de renvoyer une instance existante de DbContext ou d'instancier une nouvelle instance. .
public class DataTools
{
private AppContext _context;
protected AppContext Context => _context ?? (_context = new AppContext());
}
pubic class YourApp : DataTools
{
public void DoLotsOfThings()
{
var = Context.SomeTable.Where(s => s.....);
var stuff = GetSomeThing();
foreach(){}
}
Public string GetSomething()
{
return Context.AnotherTable.First(s => s....).Value;
}
}