Frage

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

War es hilfreich?

Lösung

Vor ein paar Monaten habe ich einen Blogbeitrag über Fluent Interfaces und LINQ geschrieben, in dem eine Erweiterungsmethode verwendet wurde IQueryable<T> und eine weitere Klasse, um die folgende natürliche Art der Paginierung 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-Codegalerie-Seite erhalten: Pipelines, Filter, Fluent API und LINQ to SQL.

Andere Tipps

Es ist ganz einfach mit dem Skip Und Take Erweiterungsmethoden.

var query = from i in ideas
            select i;

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

Ich habe das etwas anders gelöst als die anderen, da ich meinen eigenen Paginator mit einem Repeater erstellen musste.Deshalb habe ich zunächst eine Sammlung von Seitenzahlen für die Sammlung der Artikel 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]

Damit könnte ich die Artikelsammlung leicht in eine Sammlung von „Seiten“ unterteilen.Eine Seite ist in diesem Fall nur eine Sammlung von Elementen (IEnumerable<Item>).So können Sie es nutzen Skip Und Take zusammen mit der Auswahl des Index aus dem pageRange oben erstellt:

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

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


Der einzeiliger TLDR Version wäre diese:

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

Was wie folgt verwendet werden kann:

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

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

Diese Frage ist etwas alt, aber ich wollte meinen Paging-Algorithmus posten, der den gesamten Vorgang (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 es Ihnen jedoch um Leistung geht, und im Produktionscode geht es uns allen um Leistung, sollten Sie nicht das oben gezeigte Paging von LINQ verwenden, sondern das zugrunde liegende IEnumerator Paging selbst implementieren.Tatsächlich ist es genauso einfach wie der oben gezeigte LINQ-Algorithmus, aber leistungsfähiger:

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 Verwendung Skip() für mehrere Male auf „kaskadierende Weise“ bedeutet, dass der „Zeiger“ der Iteration, an der er zuletzt übersprungen wurde, nicht wirklich gespeichert wird.- Stattdessen wird die ursprüngliche Sequenz mit Skip-Aufrufen vorab geladen, was dazu führt, dass die bereits „verbrauchten“ Seiten immer wieder „verbraucht“ werden.- Das können Sie selbst beweisen, wenn Sie die Sequenz erstellen ideas sodass es zu Nebenwirkungen kommt.-> Auch 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 mit der Iteration von 40+ beginnen.Die Variante mit IEnumerabledirekt an die Schnittstelle von 's, merkt sich stattdessen die Position des Endes der letzten logischen Seite, sodass kein explizites Überspringen erforderlich ist und Nebenwirkungen nicht wiederholt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top