Comment LINQ retarde-t-il l’exécution dans une instruction using

Imaginez que j’ai les éléments suivants:

private IEnumerable MyFunc(parameter a) { using(MyDataContext dc = new MyDataContext) { return dc.tablename.Select(row => row.parameter == a); } } private void UsingFunc() { var result = MyFunc(new a()); foreach(var row in result) { //Do something } } 

Selon la documentation, l’exécution de linq différera jusqu’à ce que j’énumère le résultat, ce qui se produit dans la ligne située au début de chaque chaîne. Cependant, l’instruction using doit forcer la collecte fiable de l’object à la fin de l’appel de MyFunct ().

Que se passe-t-il réellement, quand le broyeur fonctionnera-t-il et / ou le résultat s’exécutera-t-il?

La seule chose à laquelle je peux penser est que l’exécution différée est calculée au moment de la compilation. L’appel réel est donc déplacé par le compilateur vers la première ligne de foreach, ce qui entraîne l’exécution correcte de l’utilisation, mais ne s’exécute pas avant la ligne foreach? Y a-t-il un gourou qui peut aider?

EDIT: NOTE: Ce code fonctionne, je ne comprends tout simplement pas comment.

J’ai lu un peu et j’ai compris dans mon code que j’avais appelé la méthode d’extension ToList () qui, bien sûr, énumère le résultat. Le comportement de la réponse cochée est parfaitement correct pour la question posée.

Désolé pour toute confusion.

Je m’attendrais à ce que cela ne fonctionne tout simplement pas; la Select est différée, aucune donnée n’a donc été consommée à ce stade. Cependant, puisque vous avez disposé le contexte de données (avant de quitter MyFunc ), il ne pourra jamais obtenir de données. Une meilleure option consiste à transmettre le contexte de données à la méthode, afin que le consommateur puisse choisir la durée de vie. De même, je vous recommanderais de renvoyer IQueryable afin que le consommateur puisse “composer” le résultat (c’est-à-dire append OrderBy / Skip / Take / Where etc., pour qu’il ait un impact sur la requête finale):

 // this could also be an instance method on the data-context internal static IQueryable MyFunc( this MyDataContext dc, parameter a) { return dc.tablename.Where(row => row.parameter == a); } private void UsingFunc() { using(MyDataContext dc = new MyDataContext()) { var result = dc.MyFunc(new a()); foreach(var row in result) { //Do something } } } 

Mise à jour: si (les commentaires) ne veulent pas différer l’exécution (c’est-à-dire que vous ne voulez pas que l’appelant gère le contexte de données), vous devez évaluer les résultats. Vous pouvez le faire en appelant .ToList() ou .ToArray() sur le résultat pour mettre les valeurs en mémoire tampon.

 private IEnumerable MyFunc(parameter a) { using(MyDataContext dc = new MyDataContext) { // or ToList() etc return dc.tablename.Where(row => row.parameter == a).ToArray(); } } 

Si vous souhaitez le garder différé dans ce cas, vous devez utiliser un “bloc iterator”:

 private IEnumerable MyFunc(parameter a) { using(MyDataContext dc = new MyDataContext) { foreach(SomeType row in dc .tablename.Where(row => row.parameter == a)) { yield return row; } } } 

Ceci est maintenant différé sans passer le contexte de données autour.

Je viens de poster une autre solution d’exécution différée à ce problème ici , y compris cet exemple de code:

 IQueryable MyFunc(ssortingng myValue) { return from dc in new MyDataContext().Use() from row in dc.MyTable where row.MyField == myValue select row; } void UsingFunc() { var result = MyFunc("MyValue").OrderBy(row => row.SortOrder); foreach(var row in result) { //Do something } } 

La méthode d’extension Use() agit essentiellement comme un bloc différé.