Pular para o conteúdo
.NETConceito e implementação de paging
Módulo 05Consultas Avançadas

Conceito e implementação de paging

intermediario 40 min de leitura·Atualizado · .NET 9
Resumo

Paginação evita retornar coleções gigantes de uma vez. Implementamos Skip/Take no repositório, encapsulamos parâmetros num objeto e expomos metadados de paginação no cabeçalho X-Pagination.

1. Objetivos de Aprendizagem#

  • Justificar a necessidade de paginação.
  • Implementar paging com Skip/Take no EF Core.
  • Retornar metadados de paginação de forma padronizada.

2. Pré-requisitos#

  • Pilha assíncrona (Módulo 28).

3. Conceito#

O diagrama abaixo resume o fluxo principal.

Carregando diagrama…

Sem paginação, GET /produtos pode retornar milhares de registros — lento, caro e arriscado. Paginação fatia o resultado em páginas (pageNumber, pageSize). O porquê: previsibilidade de tamanho de resposta, menor uso de memória e melhor experiência do cliente.

A contagem total permite ao cliente saber quantas páginas existem; expomos isso no cabeçalho X-Pagination.

4. Mão na Massa#

4.1. Setup#

Sem pacotes novos.

4.2. Implementação Passo a Passo#

  1. Parâmetros com limites:
C#
public class ProdutoParameters
{
    private const int MaxPageSize = 50;
    public int PageNumber { get; set; } = 1;

    private int _pageSize = 10;
    public int PageSize
    {
        get => _pageSize;
        set => _pageSize = value > MaxPageSize ? MaxPageSize : value;
    }
}
  1. Lista paginada com metadados:
C#
public class PagedList<T> : List<T>
{
    public int CurrentPage { get; }
    public int TotalPages { get; }
    public int PageSize { get; }
    public int TotalCount { get; }

    public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
    {
        TotalCount = count;
        PageSize = pageSize;
        CurrentPage = pageNumber;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);
        AddRange(items);
    }

    public static async Task<PagedList<T>> CreateAsync(
        IQueryable<T> source, int pageNumber, int pageSize, CancellationToken ct)
    {
        var count = await source.CountAsync(ct);
        var items = await source.Skip((pageNumber - 1) * pageSize)
                                .Take(pageSize).ToListAsync(ct);
        return new PagedList<T>(items, count, pageNumber, pageSize);
    }
}
  1. Repositório e controller:
C#
var pagedProdutos = await PagedList<Produto>.CreateAsync(
    query, parameters.PageNumber, parameters.PageSize, ct);

Response.Headers["X-Pagination"] = JsonSerializer.Serialize(new
{
    pagedProdutos.CurrentPage, pagedProdutos.TotalPages,
    pagedProdutos.PageSize, pagedProdutos.TotalCount
});
return Ok(_mapper.Map<IEnumerable<ProdutoDto>>(pagedProdutos));

4.3. Executando#

Shell
curl -i "http://localhost:5000/api/produtos?pageNumber=2&pageSize=5"
# resposta inclui o header X-Pagination

5. Exemplo Completo#

O X-Pagination entrega ao cliente tudo que ele precisa para renderizar controles de página sem consultas extras.

6. Boas Práticas e Armadilhas#

FaçaEvite
Impor um PageSize máximoDeixar o cliente pedir 1.000.000 de itens
Sempre ordenar antes de paginarSkip/Take sem OrderBy (ordem não determinística)
Expor metadados de paginaçãoRetornar só os itens sem contexto de total
Atenção

Skip/Take sem OrderBy produz resultados imprevisíveis — o banco não garante ordem. Sempre ordene por uma chave estável.

7. Segurança e Produção#

  • O limite de PageSize protege contra respostas gigantes e ataques de exaustão.
  • Em tabelas muito grandes, considere keyset pagination (cursor) em vez de Skip, que degrada em offsets altos.

8. Exercícios#

  • Fácil: ajuste o MaxPageSize e valide o efeito.
  • Médio: retorne 400 quando pageNumber < 1.
  • Desafio: implemente keyset pagination usando o último id como cursor.

9. Resumo#

Paginação fatia coleções com Skip/Take, sempre com ordenação e limite de tamanho, expondo metadados via X-Pagination. Para grandes volumes, keyset pagination escala melhor.

10. Próximos Passos#

Módulo 31: filtragem dos resultados por critérios.

11. Referências#

30-01 — Conceito e implementação de paging | Curso ASP.NET Core