Share via


Paginação

Paginação refere-se à recuperação de resultados em páginas, em vez de tudo de uma vez. Isso normalmente é feito para conjuntos de resultados grandes, em que uma interface do usuário é mostrada que permite que o usuário navegue até a próxima página ou página anterior dos resultados.

Aviso

Independentemente do método de paginação usado, certifique-se sempre de que sua ordenação seja totalmente exclusiva. Por exemplo, se os resultados forem ordenados apenas por data, mas puderem haver vários resultados com a mesma data, os resultados poderão ser ignorados ao paginar, pois são ordenados de forma diferente em duas consultas paginando. A ordenação por data e ID (ou qualquer outra propriedade individual ou combinação de propriedades) torna a ordenação totalmente exclusiva e evita esse problema. Observe que os bancos de dados relacionais não aplicam nenhuma ordenação por padrão, mesmo na chave primária.

Paginação de deslocamento

Uma maneira comum de implementar a paginação com bancos de dados é usar o Skip e Take (OFFSET e LIMIT no SQL). Considerando um tamanho de página de 10 resultados, a terceira página pode ser buscada com o EF Core da seguinte maneira:

var position = 20;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Skip(position)
    .Take(10)
    .ToList();

Infelizmente, embora essa técnica seja muito intuitiva, ela também tem algumas deficiências graves:

  1. O banco de dados ainda deve processar as primeiras 20 entradas, mesmo que elas não sejam retornadas ao aplicativo; isso cria uma carga de computação possivelmente significativa que aumenta com o número de linhas sendo ignoradas.
  2. Se ocorrer alguma atualização simultaneamente, sua paginação poderá acabar ignorando determinadas entradas ou mostrando-as duas vezes. Por exemplo, se uma entrada for removida à medida que o usuário estiver movendo da página 2 para 3, todo o conjunto de resultados "será movido para cima" e uma entrada será ignorada.

Paginação de conjunto de chaves

A alternativa recomendada à paginação baseada em deslocamento, às vezes chamada de paginação de conjunto de chaves ou paginação baseada em busca, é simplesmente usar uma cláusula WHERE para ignorar linhas, em vez de um deslocamento. Isso significa lembrar os valores relevantes da última entrada buscada (em vez de seu deslocamento) e solicitar as próximas linhas após essa linha. Por exemplo, supondo que a última entrada na última página que buscamos tivesse um valor de ID de 55, simplesmente faríamos o seguinte:

var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Where(b => b.PostId > lastId)
    .Take(10)
    .ToList();

Supondo que um índice seja definido no PostId, essa consulta é muito eficiente e também não é sensível a alterações simultâneas que ocorrem em valores de ID mais baixos.

A paginação de conjunto de chaves é apropriada para interfaces de paginação em que o usuário navega para frente e para trás, mas não dá suporte a acesso aleatório, em que o usuário pode ir para qualquer página específica. A paginação de acesso aleatório requer o uso da paginação de deslocamento, conforme explicado acima. Devido às deficiências da paginação de deslocamento, considere cuidadosamente se a paginação de acesso aleatório realmente é necessária para seu caso de uso ou se a navegação de página próxima/anterior é suficiente. Se a paginação de acesso aleatório for necessária, uma implementação robusta poderá usar a paginação do conjunto de chaves ao navegar para a página seguinte/anterior e deslocar a navegação ao saltar para qualquer outra página.

Várias chaves de paginação

Ao usar a paginação do conjunto de chaves, é frequentemente necessário solicitar por mais de uma propriedade. Por exemplo, a consulta a seguir pagina por data e ID:

var lastDate = new DateTime(2020, 1, 1);
var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.Date)
    .ThenBy(b => b.PostId)
    .Where(b => b.Date > lastDate || (b.Date == lastDate && b.PostId > lastId))
    .Take(10)
    .ToList();

Isso garante que a próxima página escolha exatamente onde a anterior terminou. À medida que mais chaves de ordenação são adicionadas, cláusulas adicionais podem ser adicionadas.

Observação

A maioria dos bancos de dados SQL dá suporte a uma versão mais simples e eficiente do acima, usando valores de linha: WHERE (Date, Id) > (@lastDate, @lastId). Atualmente, o EF Core não dá suporte para expressar isso em consultas LINQ, isso é acompanhado pelo #26822.

Índices

Assim como acontece com qualquer outra consulta, a indexação adequada é vital para um bom desempenho: certifique-se de ter índices em vigor que correspondam à sua ordenação de paginação. Se a ordenação por mais de uma coluna, um índice sobre essas várias colunas poderá ser definido. Isso é chamado de índice composto.

Para obter mais informações, consulte a página de documentação sobre índices.

Recursos adicionais