Pourquoi est-il possible d'énumérer une requête DbLinq après avoir appelé Dispose () sur le DataContext?
Question
Mise à jour - La réponse est apparemment que DbLinq ne met pas en œuvre correctement Dispose()
. D'oh!
Le dessous est toutes sortes d'induire en erreur - Bottom line: DbLinq est pas (encore) équivalent à LinqToSql, comme je supposais quand j'ai demandé à l'origine de cette question. Utilisez-le avec précaution!
J'utilise le modèle du référentiel avec DbLinq. Mes objets du référentiel IDisposable
mettre en œuvre, et la méthode ne Dispose()
seule chose - les appels Dispose()
sur le DataContext
. Chaque fois que j'utiliser un référentiel, je l'enveloppe dans un bloc de using
, comme ceci:
public IEnumerable<Person> SelectPersons()
{
using (var repository = _repositorySource.GetPersonRepository())
{
return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person>
}
}
Cette méthode retourne un IEnumerable<Person>
, donc si je comprends bien, pas l'interrogation de la base de données a effectivement lieu jusqu'à ce que Enumerable<Person>
est traversée (par exemple, en le convertissant en une liste ou un tableau ou en utilisant dans une boucle de foreach
), comme dans cet exemple:
var persons = gateway.SelectPersons();
// Dispose() is fired here
var personViewModels = (
from b in persons
select new PersonViewModel
{
Id = b.Id,
Name = b.Name,
Age = b.Age,
OrdersCount = b.Order.Count()
}).ToList(); // executes queries
Dans cet exemple, Dispose()
est appelé immédiatement après la mise en persons
, qui est un IEnumerable<Person>
, et qui est le seul moment où il est appelé.
Alors, trois questions:
- Comment ça marche? Comment un
DataContext
toujours disposé interroger la base de données des résultats après laDataContext
a été disposé? - Qu'est-ce que
Dispose()
fait faire? - Je l'ai entendu dire que ce n'est pas nécessaire (par exemple, voir cette question ) de disposer d'un
DataContext
, mais mon impression est que ce n'est pas une mauvaise idée. Y at-il raison pas de disposer d'unDataContext
DbLinq?
La solution
1 Comment ça marche? Comment un DataContext toujours disposé interroger la base de données des résultats après la DataContext a été disposé?
ne pas travail. Il y a quelque chose que vous n'êtes pas nous montrer. Je devine que soit votre classe référentiel ne pas disposer les DataContext
correctement / au bon moment, ou que vous écrivez perfunctorily ToList()
à la fin de chaque requête, ce qui annule complètement la transformation de la requête et l'exécution différée, vous obtenez normalement.
Essayez le code suivant dans une application de test, I garantie vous qu'il va jeter un ObjectDisposedException
:
// Bad code; do not use, will throw exception.
IEnumerable<Person> people;
using (var context = new TestDataContext())
{
people = context.Person;
}
foreach (Person p in people)
{
Console.WriteLine(p.ID);
}
Ceci est le cas le plus simple reproductible possible, et il sera toujours jeter. D'autre part, si vous écrivez people = context.Person.ToList()
à la place, les résultats de la requête ont déjà été énumérées dans le bloc using
, que je vais est mise ce qui se passe dans votre cas.
2 Qu'est-ce que Dispose () réellement faire?
Entre autres, il établit un drapeau indiquant que le DataContext
est disposé, qui est vérifié sur toutes les requêtes ultérieures et provoque la DataContext
de jeter un ObjectDisposedException
avec le message Object name: 'DataContext accessed after Dispose.'.
Il ferme également la connexion, si l'DataContext
ouvrit et a laissé ouvert.
3 Je l'ai entendu dire qu'il est pas nécessaire (par exemple, voir cette question) de disposer d'un DataContext, mais mon impression est que ce n'est pas une mauvaise idée. Y at-il raison de ne pas disposer d'un DataContext LinqToSql?
est nécessaire pour Dispose
le DataContext
, comme il est nécessaire de Dispose
tous les IDisposable
. Vous pourriez potentiellement fuir les connexions si vous ne parvenez pas à éliminer la DataContext
. Vous pouvez également fuite de mémoire si l'une des entités extraites de la DataContext
sont maintenus en vie, puisque le contexte maintient un cache d'identité interne pour l'unité de travail modèle il met en œuvre. Mais même si rien de tout cela était le cas, il ne vous concerne pas ce que la méthode Dispose
fait en interne. On suppose qu'il fait quelque chose d'important.
IDisposable
est contrat qui dit: « le nettoyage ne peut pas être automatique, vous devez me jeter lorsque vous avez terminé. » Vous avez aucune garantie de savoir si l'objet a sa propre finaliseur qui nettoie ou non après vous si vous oubliez de Dispose
. Les implémentations sont sujets à changement, ce qui est pourquoi il est pas une bonne idée de se fonder sur le comportement observé par opposition aux spécifications explicites.
La pire chose qui peut se produire si vous disposez d'un IDisposable
avec une méthode Dispose
vide est que vous perdez quelques cycles CPU. La pire chose qui peut arriver si vous ne pas de disposer d'un IDisposable
avec mise en œuvre non trivial il est que vous fuyez les ressources. Le choix est évident ici; si vous voyez un IDisposable
, ne pas oublier de le disposer.
Autres conseils
"personnes" est une collection IEnumerable, le DataContext (dépôt) est uniquement nécessaire pour effectuer l'appel .GetNew.
de / select / etc mots-clés sont le sucre syntaxique pour les méthodes d'extension ajoutés dans l'espace de noms System.Linq. Ces méthodes d'extension ajouter la fonctionnalité IEnumerable que vous utilisez dans votre requête, et non pas le DataContext. En fait, vous pouvez faire tout cela sans utiliser LINQ2SQL du tout, en créant un IEnumerable programatically démontrer.
Si vous essayez de faire un dépôt plus (DataContext) appelle l'utilisation de ces objets, qui est quand vous recevez une erreur.
La collection IEnumerable contiendra tous les enregistrements de votre dépôt, c'est pourquoi vous ne souhaitez pas le DataContext pour faire la requête.
Méthodes d'extension: http://msdn.microsoft.com/en- nous / bibliothèque / bb383977.aspx
Méthodes d'extension LINQ: http://msdn.microsoft.com/en-us/ bibliothèque / system.linq.enumerable_members.aspx
Au plus profond de l'API, vous verrez probablement un procédé utilisant un api comme celui-ci:
http://msdn.microsoft. com / fr-fr / bibliothèque / y6wy5a0f (v = VS.100) .aspx
Lorsque la commande est exécutée, l'objet de connexion associé est fermé lorsque l'objet DataReader associé est fermé.