Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Paginação refere-se à recuperação de resultados em páginas, em vez de todos de uma só vez; Isso geralmente é feito para grandes conjuntos de resultados, onde uma interface do usuário é mostrada que permite que o usuário navegue até a página seguinte ou anterior dos resultados.
Advertência
Independentemente do método de paginação utilizado, certifique-se sempre de que a sua encomenda é totalmente única. Por exemplo, se os resultados estiverem ordenados apenas por data, mas podem existir vários resultados com a mesma data, então os resultados podem ser ignorados na paginação, pois estão ordenados de forma diferente em duas consultas de paginação. Ordenar por data e ID (ou qualquer outra propriedade única ou combinação de propriedades) torna a ordem totalmente única e evita este problema. Note que as bases de dados relacionais não aplicam qualquer ordenação por defeito, mesmo na chave primária.
Observação
O Azure Cosmos DB tem o seu próprio mecanismo de paginação, consulte a página dedicada à documentação.
Paginação offset
Uma maneira comum de implementar paginação com bancos de dados é usar os Skip operadores e Take LINQ (OFFSET e LIMIT em SQL). Dado um tamanho de página de 10 resultados, a terceira página pode ser obtida com o EF Core da seguinte forma:
var position = 20;
var nextPage = await context.Posts
.OrderBy(b => b.PostId)
.Skip(position)
.Take(10)
.ToListAsync();
Infelizmente, embora esta técnica seja muito intuitiva, também apresenta algumas falhas graves:
- A base de dados ainda tem de processar as primeiras 20 entradas, mesmo que não sejam devolvidas à aplicação; isto cria uma carga computacional possivelmente significativa que aumenta com o número de linhas a serem saltadas.
- Se houver atualizações em simultâneo, a sua paginação pode acabar por saltar certas entradas ou mostrá-las duas vezes. Por exemplo, se uma entrada for removida enquanto o utilizador passa da página 2 para a 3, todo o conjunto de resultados "sube" e uma entrada será ignorada.
Paginação por keyset
A alternativa recomendada à paginação baseada em deslocamento – por vezes chamada de paginação por conjunto de chaves ou paginação baseada em busca – é usar simplesmente uma cláusula WHERE SQL para saltar linhas, em vez de um deslocamento. Isto significa recordar os valores relevantes da última entrada obtida (em vez do seu deslocamento) e solicitar as linhas seguintes após essa linha. Por exemplo, assumindo que a última entrada na última página que recuperámos tinha o valor de ID 55, faríamos simplesmente o seguinte:
var lastId = 55;
var nextPage = await context.Posts
.OrderBy(b => b.PostId)
.Where(b => b.PostId > lastId)
.Take(10)
.ToListAsync();
Assumindo que um índice está definido em PostId, esta consulta é muito eficiente e também não é sensível a quaisquer alterações simultâneas que ocorram em valores de Id mais baixos.
A paginação por keyset é adequada para interfaces de paginação onde o utilizador navega para a frente e para trás, mas não suporta acesso aleatório, podendo saltar para qualquer página específica. A paginação de acesso aleatório requer o uso de paginação deslocada, como explicado acima; Devido às limitações da paginação deslocada, considere cuidadosamente se a paginação de acesso aleatório é realmente necessária para o seu caso de uso, ou se a navegação na página seguinte/anterior é suficiente. Se for necessária paginação de acesso aleatório, uma implementação robusta pode usar paginação por conjunto de chaves ao navegar para a página seguinte/anterior e navegação por deslocamento ao saltar para qualquer outra página.
Múltiplas chaves de paginação
Ao usar paginação por keyset, torna-se frequentemente necessário ordenar por mais do que uma propriedade. Por exemplo, a consulta seguinte pagina-se por data e ID:
var lastDate = new DateTime(2020, 1, 1);
var lastId = 55;
var nextPage = await context.Posts
.OrderBy(b => b.Date)
.ThenBy(b => b.PostId)
.Where(b => b.Date > lastDate || (b.Date == lastDate && b.PostId > lastId))
.Take(10)
.ToListAsync();
Isto garante que a página seguinte começa exatamente onde a anterior terminou. À medida que mais chaves de ordenação são adicionadas, podem ser adicionadas cláusulas adicionais.
Observação
A maioria das bases de dados SQL suporta uma versão mais simples e eficiente do acima, usando valores de linha: WHERE (Date, Id) > (@lastDate, @lastId). O EF Core atualmente não suporta expressar isto em consultas LINQ, isto é monitorizado pelo #26822.
Indexes
Como em qualquer outra consulta, uma indexação adequada é vital para um bom desempenho: certifique-se de que existem índices que correspondam à sua ordem de paginação. Se ordenar por mais do que uma coluna, pode ser definido um índice sobre essas múltiplas colunas; Isto chama-se índice composto.
Para mais informações, consulte a página de documentação sobre índices.
Recursos adicionais
- Para saber mais sobre as limitações da paginação baseada em deslocamento e sobre a paginação por conjunto de chaves, consulte este artigo.
- Sessão de standup da Comunidade de Dados .NET onde discutimos a paginação e demonstramos todos os conceitos acima.
- Uma apresentação técnica aprofundada que compara a paginação offset e keyset. Embora o conteúdo trate da base de dados PostgreSQL, a informação geral é válida para outras bases de dados relacionais também.
- Para extensões sobre o EF Core que simplificam a paginação por conjunto de chaves, veja MR. EntityFrameworkCore.KeysetPagination e MR. AspNetCore.Pagination.