Question

Comment parcourir une collection dans LINQ étant donné que vous disposez d'un startIndex et un count?

Était-ce utile?

La solution

Il y a quelques mois, j'ai écrit un article de blog sur Fluent Interfaces et LINQ qui utilisait une méthode d'extension sur IQueryable<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 sur la page MSDN Code Gallery : Pipelines, filtres, API Fluent et LINQ to SQL.

Autres conseils

C'est très simple avec le Skip et Take méthodes d'extension.

var query = from i in ideas
            select i;

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

J'ai résolu ce problème un peu différemment de ce que les autres ont fait car j'ai dû créer 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'éléments 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".Dans ce cas, une page n'est qu'une collection d'éléments (IEnumerable<Item>).Voici comment procéder en utilisant Skip et Take avec la sélection de l'index dans le pageRange créé 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 ex.si vous imbriquez des répéteurs, c'est en fait facile à gérer.


Le TLDR à une seule ligne la version serait celle-ci :

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
    }
}

Cette question est un 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 la performance, et que dans le code de production, nous recherchons tous la performance, vous ne devriez pas utiliser la pagination de LINQ comme indiqué ci-dessus, mais plutôt la pagination sous-jacente. IEnumerator pour implémenter vous-même la pagination.En fait, c'est aussi simple que l'algorithme LINQ présenté 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:L'inconvénient de l'utilisation Skip() car plusieurs fois de manière "en cascade", il ne stockera pas vraiment le "pointeur" de l'itération, là où il a été ignoré pour la dernière fois.- Au lieu de cela, la séquence originale sera chargée en premier 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équence ideas de sorte que cela produit 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 à nouveau exécutés, avant de commencer à itérer 40+.La variante utilisant IEnumerableL'interface de directement se souviendra 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 se répéteront pas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top