.net 3.5 - Paging d'une collection avec LINQ

Translate

Comment parcourez-vous une collection dans LINQ étant donné que vous avez unstartIndexet uncount?

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

Il y a quelques mois, j'ai écrit un article de blog sur Fluent Interfaces et LINQ qui utilisait une méthode d'extension surIQueryable<T>et une autre classe pour fournir la manière naturelle suivante de paginer une collection LINQ.

var query = from i in ideas
            select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);

Vous pouvez obtenir le code à partir de la page MSDN Code Gallery:Pipelines, filtres, API Fluent et LINQ to SQL.

La source
Translate

C'est très simple avec leSkipetTakeméthodes d'extension.

var query = from i in ideas
            select i;

var paggedCollection = query.Skip(startIndex).Take(count);
La source
Translate

J'ai résolu cela un peu différemment de ce que les autres ont car j'ai dû faire mon propre paginateur, avec un répéteur. J'ai donc d'abord fait une collection de numéros de page pour la collection d'articles que j'ai:

// assumes that the item collection is "myItems"

int pageCount = (myItems.Count + PageSize - 1) / PageSize;

IEnumerable<int> pageRange = Enumerable.Range(1, pageCount);
   // pageRange contains [1, 2, ... , pageCount]

En utilisant cela, je pourrais facilement partitionner la collection d'éléments en une collection de "pages". Une page dans ce cas est juste une collection d'éléments (IEnumerable<Item>). Voici comment vous pouvez le faire en utilisantSkipetTakeainsi que la sélection de l'index dans lepageRangecréé ci-dessus:

IEnumerable<IEnumerable<Item>> pageRange
    .Select((page, index) => 
        myItems
            .Skip(index*PageSize)
            .Take(PageSize));

Bien sûr, vous devez gérer chaque page comme une collection supplémentaire, mais par exemple, si vous imbriquez des répéteurs, c'est en fait facile à gérer.


lemonotouche TLDRla version serait la suivante:

var pages = Enumerable
    .Range(0, pageCount)
    .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));

Qui peut être utilisé comme ceci:

for (Enumerable<Item> page : pages) 
{
    // handle page

    for (Item item : page) 
    {
        // handle item in page
    }
}
La source
Translate

Cette question est quelque peu ancienne, mais je voulais publier mon algorithme de pagination qui montre toute la procédure (y compris l'interaction de l'utilisateur).

const int pageSize = 10;
const int count = 100;
const int startIndex = 20;

int took = 0;
bool getNextPage;
var page = ideas.Skip(startIndex);

do
{
    Console.WriteLine("Page {0}:", (took / pageSize) + 1);
    foreach (var idea in page.Take(pageSize))
    {
        Console.WriteLine(idea);
    }

    took += pageSize;
    if (took < count)
    {
        Console.WriteLine("Next page (y/n)?");
        char answer = Console.ReadLine().FirstOrDefault();
        getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer);

        if (getNextPage)
        {
            page = page.Skip(pageSize);
        }
    }
}
while (getNextPage && took < count);

Cependant, si vous recherchez les performances, et dans le code de production, nous recherchons tous les performances, vous ne devriez pas utiliser la pagination de LINQ comme indiqué ci-dessus, mais plutôt le sous-jacentIEnumeratorpour implémenter la pagination vous-même. En fait, c'est aussi simple que l'algorithme LINQ montré ci-dessus, mais plus performant:

const int pageSize = 10;
const int count = 100;
const int startIndex = 20;

int took = 0;
bool getNextPage = true;
using (var page = ideas.Skip(startIndex).GetEnumerator())
{
    do 
    {
        Console.WriteLine("Page {0}:", (took / pageSize) + 1);

        int currentPageItemNo = 0;
        while (currentPageItemNo++ < pageSize && page.MoveNext())
        {
            var idea = page.Current;
            Console.WriteLine(idea);
        }

        took += pageSize;
        if (took < count)
        {
            Console.WriteLine("Next page (y/n)?");
            char answer = Console.ReadLine().FirstOrDefault();
            getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer);
        }
    }
    while (getNextPage && took < count);
}

Explication: les inconvénients de l'utilisationSkip()pour plusieurs fois de manière "en cascade", c'est qu'il ne stockera pas vraiment le "pointeur" de l'itération, là où il a été sauté pour la dernière fois. - Au lieu de cela, la séquence d'origine sera chargée avec des appels de saut, ce qui conduira à "consommer" les pages déjà "consommées" encore et encore. - Vous pouvez le prouver vous-même, lorsque vous créez la séquenceideasafin qu'il produise des effets secondaires. -> Même si vous avez sauté 10-20 et 20-30 et que vous souhaitez traiter 40+, vous verrez tous les effets secondaires de 10-30 être exécutés à nouveau, avant de commencer à itérer 40+. La variante utilisantIEnumerableL'interface de directement, se souviendra à la place de la position de la fin de la dernière page logique, donc aucun saut explicite n'est nécessaire et les effets secondaires ne seront pas répétés.

La source