2008-08-01 25 views

Antwort

34

Vor ein paar Monaten schrieb ich einen Blog-Post über Fluent Interfaces und LINQ, die eine Erweiterungsmethode auf IQueryable<T> und eine andere Klasse verwendet, um die folgende natürliche Weise Paginieren eine LINQ-Sammlung zur Verfügung zu stellen.

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

Sie können den Code aus der MSDN Code Gallery Seite erhalten: Pipelines, Filters, Fluent API and LINQ to SQL.

59

Es ist sehr einfach mit den Erweiterungsmethoden Skip und Take.

var query = from i in ideas 
      select i; 

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

Ich glaube, dass es in Ordnung ist, so etwas zu tun. Er hat vielleicht eine Antwort, aber vielleicht will er auch sehen, was andere Leute mitbringen können. –

+11

Dies wurde ursprünglich am ersten Tag der Beta-Phase von StackOverflow veröffentlicht, also die 66 für die Artikel-ID. Ich habe das System getestet, für Jeff. Plus es schien wie nützliche Informationen anstelle der üblichen Test Mist, der manchmal aus Beta-Tests kommt. –

6

Diese Frage ist etwas alt, aber ich wollte meinen Paging-Algorithmus veröffentlichen, 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 sind, und in der Produktion Code, sind wir alle nach Leistung, sollten Sie nicht Paging LINQ verwenden, wie oben gezeigt, sondern IEnumerator die darunter liegende, sich Paging zu implementieren. Wie in der Tat ist es so einfach wie der LINQ-Algorithmus, der oben gezeigt, aber leistungsfähigere:

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 von Skip() für mehrfach in einem „kaskadenartig“ verwendet, ist, dass es wird nicht wirklich den "Zeiger" der Iteration speichern, wo sie zuletzt übersprungen wurde. - Stattdessen wird die ursprüngliche Sequenz mit Skip-Anrufen geladen, was dazu führt, dass die bereits "konsumierten" Seiten immer wieder "konsumiert" werden. - Das kannst du selbst beweisen, wenn du die Sequenz ideas so erstellst, dass es zu Nebenwirkungen kommt. -> Auch wenn Sie 10-20 und 20-30 übersprungen haben und 40+ verarbeiten möchten, sehen Sie alle Nebenwirkungen von 10-30 erneut ausgeführt werden, bevor Sie anfangen, 40+ zu wiederholen. Die Variante, die direkt die Schnittstelle IEnumerable verwendet, erinnert sich stattdessen an die Position des Endes der letzten logischen Seite, so dass kein explizites Überspringen erforderlich ist und die Nebenwirkungen nicht wiederholt werden.

10

Ich löste das ein bisschen anders als das, was die anderen haben, da ich meinen eigenen Paginator mit einem Repeater machen musste. Also machte ich zum ersten Mal eine Sammlung von Seitenzahlen für die Sammlung von Gegenständen, 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] 

Mit diesem ich einfach das Element Sammlung in eine Sammlung von „Seiten“ partitionieren können. Eine Seite in diesem Fall ist nur eine Sammlung von Elementen (IEnumerable<Item>). Dies ist, wie Sie es tun können Skip und Take zusammen mit der Auswahl des Index aus den oben erstellten pageRange mit:

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

Natürlich

müssen Sie jede Seite als eine zusätzliche Sammlung handhaben, aber z.B. Wenn Sie Repeater verschachteln, ist das eigentlich einfach zu handhaben.


Die Einzeiler TLDR Version wäre dies:

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

die als diese verwendet werden können:

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

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