2010-07-09 5 views
2

Pour chaque requête LINQ que j'ai écrite, j'ai toujours utilisé une boucle foreach pour parcourir le résultat. Maintenant, j'ai un programme où je veux obtenir les 50 premières lignes du résultat, faire des calculs avec ces lignes, puis obtenir les 50 prochaines lignes du résultat, etc.Accès aux lignes dans un résultat LINQ sans boucle Foreach?

Quelle est la bonne façon de procéder? en utilisant LINQ et C#?

Répondre

5

.Skip(). Take() sera la meilleure méthode (en ce qui concerne la clarté) dans ce cas. Par exemple:

var results = db.Table.Select(); // Your query here 

int rowCount = results.Count(); // Count rows once and store 

for(int i = 0; i <= rowCount; i += 50) 
{ 
    var paged = results.Skip(i).Take(50); 

    // do the calculations with the groups of 50 here 
} 

Il convient de noter que, même si cela semble plus clair (vous prenez des groupes de 50 à partir des résultats), chaque fois que vous appelez Skip() et Take() il y a une chance que le la collection doit être énumérée à nouveau ... ce qui est en fait moins efficace que d'utiliser simplement foreach pour énumérer les résultats une seule fois.

+0

Mere secondes !!! – Will

+3

Cela va itérer sur l'ensemble plusieurs fois si l'objet est 'IEnumerable <>' –

+0

Des scénarios spécifiques pourraient également montrer plus d'efficacité en déplaçant la sélection dans la construction de la boucle. Je ne suis pas sûr non plus dans quels cas. Prendre échouera d'une énumération vide (iow, rowCount étant inutile). – Marc

2

Sans savoir quoi que ce soit sur la nature des données et/ou les calculs, la "bonne norme" pour ce faire serait probablement une boucle foreach!

Il serait utile de fournir des informations sur la nature des données et les calculs que vous souhaitez effectuer. En fonction de cela, la meilleure réponse pourrait être foreach, Skip/Take, GroupBy ou peut-être quelque chose d'autre.

-3

Quelque chose à cet effet

var page = (From c In db.Customers Order By c.ContactName, c.City Descending 
       Select c).Skip(50).Take(50) 
4

Vous pouvez utiliser un groupe par-à-dire:

data.Select((item, index) => new {item, index}).GroupBy(g => g.index/50) 

alors vous feriez votre opération sur chaque groupe.

semblable à ceci: How to use Linq to group every N number of rows

0

L'utilisation .Skip(...).Take(...) travaillera pour la pagination. Mais quelque chose à considérer est qu'à nouveau un IEnumerable<...> recalculera l'énumérable chaque fois qu'il est traité. Est-ce que c'est quelque chose qui est consommé au lieu d'être réinitialisable, vous pourriez avoir des problèmes. Cet ensemble de méthodes peut résoudre ce problème.

public static class EnumerableEx 
{ 
    public static IEnumerable<IEnumerable<T>> AsPages<T>(
     this IEnumerable<T> set, int pageLength) 
    { 
     using (var e = set.GetEnumerator()) 
      while (true) 
       yield return GetPage(e, pageLength); 
    } 
    private static IEnumerable<T> GetPage<T>(
     IEnumerator<T> set, int pageLength) 
    { 
     for (var position = 0; 
      position < pageLength && set.MoveNext(); 
      position++) 
      yield return set.Current; 
    } 
} 

... Voici un exemple d'utilisation expliquant pourquoi c'est important.

class Program 
{ 
    private static int _last = 0; 
    private static IEnumerable<int> GetValues() 
    { 
     while (true) 
      yield return _last++; 
    } 

    static void Main(string[] args) 
    { 
     for (var i = 0; i < 3; i++) 
      foreach (var value in GetValues().Skip(5 * i).Take(5)) 
       Console.Write(value + ","); 
     // 0,1,2,3,4,10,11,12,13,14,25,26,27,28,29, 
     Console.WriteLine(); 
     _last = 0; 

     foreach (var page in GetValues().AsPages(5).Take(3)) 
      foreach (var value in page) 
       Console.Write(value + ","); 
     // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14, 
    } 
}