Como você folheia uma coleção no LINQ, considerando que você tem um startIndex
e um count
?
84
Alguns meses atrás, escrevi uma postagem no blog sobre interfaces fluentes e LINQ que usava um método de extensão em IQueryable<T>
e outra classe para fornecer a seguinte maneira natural de paginar uma coleção LINQ.
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Você pode obter o código na página MSDN Code Gallery: Pipelines, Filters, Fluent API e LINQ to SQL .
É muito simples com os métodos de extensão Skip
e Take
.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
Resolvi isso de forma um pouco diferente do que os outros, pois tive que fazer meu próprio paginador, com repetidor. Então, primeiro fiz uma coleção de números de página para a coleção de itens que tenho:
// 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]
Usando isso, eu poderia facilmente particionar a coleção de itens em uma coleção de "páginas". Uma página, neste caso, é apenas uma coleção de itens (
IEnumerable<Item>
). É assim que você pode fazer usandoSkip
eTake
junto com a seleção do índicepageRange
criado acima:IEnumerable<IEnumerable<Item>> pageRange .Select((page, index) => myItems .Skip(index*PageSize) .Take(PageSize));
Claro que você tem que lidar com cada página como uma coleção adicional, mas, por exemplo, se você estiver aninhando repetidores, isso é realmente fácil de lidar.
A versão TLDR de uma linha seria esta:
var pages = Enumerable .Range(0, pageCount) .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
Que pode ser usado assim:
for (Enumerable<Item> page : pages) { // handle page for (Item item : page) { // handle item in page } }
fonte
Esta pergunta é um pouco antiga, mas eu queria postar meu algoritmo de paginação que mostra todo o procedimento (incluindo a interação do usuário).
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);
No entanto, se você está atrás de desempenho, e no código de produção, estamos todos atrás de desempenho, você não deve usar a paginação do LINQ como mostrado acima, mas sim a base
IEnumerator
para implementar você mesmo a paginação. Na verdade, é tão simples quanto o algoritmo LINQ mostrado acima, mas tem mais desempenho: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); }
Explicação: A desvantagem de usar
Skip()
várias vezes em uma "maneira em cascata" é que ele não armazenará realmente o "ponteiro" da iteração onde foi ignorado pela última vez. - Em vez disso, a sequência original será carregada antecipadamente com chamadas ignoradas, o que levará ao "consumo" das páginas já "consumidas" continuamente. - Você mesmo pode provar isso, ao criar a sequênciaideas
para que ela produza efeitos colaterais. -> Mesmo se você tiver pulado 10-20 e 20-30 e quiser processar 40+, verá todos os efeitos colaterais de 10-30 sendo executados novamente, antes de começar a iterar 40+. A variante usandoIEnumerable
a interface de diretamente, em vez disso, lembrará a posição do final da última página lógica, de modo que nenhum salto explícito é necessário e os efeitos colaterais não se repetirão.fonte