Paging einer Sammlung mit LINQ

  • Wie blättern Sie durch eine Sammlung in LINQ, wenn Sie startIndex und count haben?

    04 May 2012
    mattytommoSkooz
4 answers
  • Mit den Erweiterungsmethoden Skip und Take ist das sehr einfach.

     var query = from i in ideas
                select i;
    
    var paggedCollection = query.Skip(startIndex).Take(count);
     
    04 May 2012
    mattytommoSkooz
  • Vor ein paar Monaten schrieb ich einen Blogbeitrag über Fluent Interfaces und LINQ, der eine Erweiterungsmethode für IQueryable<T> und eine andere Klasse verwendete, um die folgende natürliche Methode zum Paginieren einer LINQ-Sammlung bereitzustellen.

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

    Sie können den Code von der MSDN Code Gallery-Seite erhalten: Pipelines, Filter, fließende API und LINQ to SQL .

    09 August 2012
    Adipaul y cho
  • Ich habe das etwas anders gelöst als die anderen, da ich mit einem Repeater einen eigenen Paginator machen musste. Also habe ich zuerst eine Sammlung von Seitennummern für die Sammlung von Artikeln erstellt, die ich habe:

     // 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]
     

    Verwendung dieses I könnte die Elementsammlung leicht in eine Sammlung von "Seiten" aufteilen. Eine Seite ist in diesem Fall nur eine Sammlung von Elementen (IEnumerable<Item>). So können Sie dies mit Skip und Take zusammen mit der Auswahl des Indexes aus dem oben erstellten pageRange tun:

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

    Natürlich müssen Sie jede Seite als zusätzliche Sammlung behandeln, aber z Wenn Sie Repeater verschachteln, ist dies eigentlich einfach zu handhaben.


    Die TLDR-Version wäre dies:

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

    Was kann wie folgt verwendet werden:

     for (Enumerable<Item> page : pages) 
    {
        // handle page
    
        for (Item item : page) 
        {
            // handle item in page
        }
    }
     
    20 March 2012
    SpoikePabloBlamirez
  • Diese Frage ist etwas alt, aber ich wollte meinen Paging-Algorithmus posten, der die gesamte Prozedur (einschließlich Benutzerinteraktion) zeigt.

     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);
     

    Wenn Sie jedoch nach Leistung streben und im Produktionscode alle nach Leistung streben, sollten Sie nicht das Paging von LINQ wie oben gezeigt verwenden, sondern das zugrunde liegende IEnumerator zum Implementieren Paging dich selbst Es ist tatsächlich so einfach wie der oben gezeigte LINQ-Algorithmus, jedoch performanter:

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

    Erläuterung: Der Nachteil der mehrfachen Verwendung von Skip() auf "kaskadierende Weise" besteht darin, dass der "Zeiger" der Iteration, an der er zuletzt übersprungen wurde, nicht wirklich gespeichert wird. - Stattdessen wird die ursprüngliche Sequenz mit Überspringaufrufen vorgeladen, was dazu führt, dass die bereits "verbrauchten" Seiten immer wieder "verbraucht" werden. - Sie können das selbst beweisen, wenn Sie die Sequenz ideas so erstellen, dass sie Nebenwirkungen erzeugt. - & gt; Selbst wenn Sie 10-20 und 20-30 übersprungen haben und 40+ verarbeiten möchten, werden alle Nebenwirkungen von 10-30 erneut ausgeführt, bevor Sie 40 + durchlaufen. Die Variante verwendet IEnumerable 's Schnittstelle direkt, wird sich stattdessen die Position des Endes der letzten logischen Seite merken, sodass kein explizites Überspringen erforderlich ist und Nebeneffekte nicht wiederholt werden.

    03 March 2018
    Nico