2008-08-01 26 views

Respuesta

34

Hace unos meses escribí un post sobre Fluent interfaces y LINQ, que utiliza un método de extensión en IQueryable<T> y otra clase para proporcionar la siguiente forma natural de paginar una colección de LINQ.

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

que pueda obtener el código de la Galería de código MSDN Página: Pipelines, Filters, Fluent API and LINQ to SQL.

59

Es muy simple con los métodos de extensión Skip y Take.

var query = from i in ideas 
      select i; 

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

yo creo que está bien hacer algo como esto. Él podría tener una respuesta, pero tal vez él quiere ver lo que otras personas pueden aportar también. –

+11

Esto se publicó originalmente durante el primer día del período beta de StackOverflow, por lo tanto, el 66 para la ID del artículo. Estaba probando el sistema, para Jeff. Además, me pareció información útil en lugar de la típica basura de prueba que a veces sale de las pruebas beta. –

6

Esta pregunta es algo antigua, pero quería publicar mi algoritmo de paginación que muestra todo el procedimiento (incluida la interacción del usuario).

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

Sin embargo, si usted está después de la actuación, y en el código de producción, estamos todos después de la actuación, no se debe utilizar paginación de LINQ como se muestra arriba, sino más bien el subyacente IEnumerator para implementar la paginación mismo. Como cuestión de hecho, es tan simple como la LINQ-algoritmo mostrado anteriormente, pero más 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); 
} 

Explicación: La desventaja de usar Skip() para múltiples veces en una "forma en cascada" es, que lo hará realmente no almacena el "puntero" de la iteración, donde se saltó por última vez. - En cambio, la secuencia original se cargará de frente con saltos de llamadas, lo que conducirá a "consumir" las páginas ya "consumidas" una y otra vez. - Puede probarlo usted mismo, cuando crea la secuencia ideas para que produzca efectos secundarios. -> Incluso si se salteó 10-20 y 20-30 y quiere procesar más de 40, verá todos los efectos secundarios de 10-30 ejecutándose nuevamente, antes de comenzar a iterar 40+. La variante que usa la interfaz IEnumerable directamente, en su lugar recordará la posición del final de la última página lógica, por lo que no se requiere omisión explícita y los efectos secundarios no se repetirán.

10

Lo resolví de forma un poco diferente de lo que tienen los demás ya que tuve que hacer mi propio paginador, con un repetidor. Así que hice por primera vez una colección de números de página para la colección de artículos que tengo:

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

El uso de este podría fácilmente dividir el punto de recogida en una colección de "páginas". Una página en este caso es solo una colección de artículos (IEnumerable<Item>). Esta es la forma en que puede hacerlo utilizando Skip y Take junto con la selección del índice de la pageRange creado anteriormente:

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

Por supuesto, usted tiene que manejar cada página como una colección, pero por ejemplo, si anida repetidores, entonces esto es realmente fácil de manejar.


La sola línea versión TLDR sería la siguiente:

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

que puede ser utilizado como esto:

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

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