questions - Paginación de una colección con LINQ




paginar lista c# (3)

¿Cómo navega por una colección en LINQ dado que tiene un startIndex y un count ?


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

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 busca rendimiento y en el código de producción, todos estamos después del rendimiento, no debe usar la paginación de LINQ como se muestra arriba, sino el IEnumerator subyacente para implementar la paginación usted mismo. De hecho, es tan simple como el algoritmo LINQ que se muestra arriba, pero más eficaz:

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() varias veces de forma "en cascada" es que realmente no almacenará el "puntero" de la iteración, donde se omitió por última vez. - En cambio, la secuencia original se cargará por adelantado con llamadas salteadas, lo que conducirá a "consumir" las páginas ya "consumidas" una y otra vez. - Puede probarlo usted mismo cuando crea las ideas secuencia para que produzca efectos secundarios. -> Incluso si ha omitido 10-20 y 20-30 y desea procesar más de 40, verá que todos los efectos secundarios de 10-30 se ejecutan nuevamente, antes de comenzar a repetir 40+. La variante que usa la interfaz de IEnumerable directamente, en cambio, recordará la posición del final de la última página lógica, por lo que no se necesita omitir explícitamente y los efectos secundarios no se repetirán.


Resolví esto un poco diferente de lo que tienen los demás, ya que tuve que hacer mi propio paginador, con un repetidor. Así que primero hice 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]

Utilizando esto, podría fácilmente dividir la colección de elementos en una colección de "páginas". Una página en este caso es solo una colección de elementos ( IEnumerable<Item> ). Así es como puede hacerlo usando Skip y Take junto con la selección del índice de la página pageRange creado anteriormente:

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

Por supuesto, debe manejar cada página como una colección adicional, pero, por ejemplo, si está anidando repetidores, esto es realmente fácil de manejar.

La versión TLDR de una línea sería esta:

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

Que se puede usar así:

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

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




.net-3.5