Documentação: Paginação com Entity Framework Core e PaginacaoDto
Visão Geral
Esta documentação explica como implementar paginação eficiente usando o PaginacaoDto do projeto com Entity Framework Core, seguindo as melhores práticas para performance e escalabilidade.
Estrutura do PaginacaoDto
public class PaginacaoDto
{
/// <summary>
/// Indica se há próxima página
/// </summary>
public bool HasNextPage { get; set; }
/// <summary>
/// Índice da página atual (baseado em 0)
/// </summary>
public int PageIndex { get; set; }
/// <summary>
/// Tamanho da página
/// </summary>
public int PageSize { get; set; }
}
Implementação
1. Service - Implementação
// CondicaoPagamentoService.cs
using Microsoft.EntityFrameworkCore;
using SGApi.Data;
using SGApi.Infra;
namespace SGApi.CondicoesPagamentos
{
public class CondicaoPagamentoService
{
private readonly SglinxDbContext _context;
public CondicaoPagamentoService(SglinxDbContext context)
{
_context = context;
}
public async Task<CondicoesPagamentosResponseDto> SincronizarCondicoesPagamentosAsync(PaginacaoDto paginacao)
{
// Construir query base
var query = _context.CondicaoPagamento
.Where(x => x.Ativo == "1" && x.Tipo != 9)
.AsNoTracking(); //Melhor performance para consultas somente leitura
// Aplicar paginação com .Skip e .Take
var items = await query
.Skip(paginacao.PageIndex * paginacao.PageSize)
.Take(paginacao.PageSize + 1) // Aqui eu usei o .Take(size + 1) para sempre pegar um registro a mais e verificar se existe um registro a mais para verificar se existe proxima pagina. Você deve sempre usar essa abordagem
.ToListAsync();
// Mapeamento para DTOs em caso de dúvidas consulte tutorial_mappings.md
var dtos = items.Select(entity => entity.MapTo<CondicaoPagamentoItemDto>()).ToList();
var response = new CondicoesPagamentosResponseDto();
response.Items = items.Select(entity => entity.MapTo<CondicaoPagamentoItemDto>()).Take(paginacao.PageSize).ToList();
// Calculando a paginação a partir da classe PaginacaoService
response.Pagination = _paginacaoService.CalcularPaginacao(paginacao.PageIndex, paginacao.PageSize, items.Count);
return response;
}
}
}
2. Controller - Implementação
// CondicoesPagamentosController.cs
[ApiController]
[Route("cadastros/condicoes-pagamentos")]
[Authorize]
public class CondicoesPagamentosController : ControllerBase
{
private readonly CondicaoPagamentoService _service;
public CondicoesPagamentosController(CondicaoPagamentoService service)
{
_service = service;
}
[HttpPost("sincronizacao")]
public async Task<ActionResult<CondicoesPagamentosResponseDto>> SincronizacaoCondicoesPagamentos(
[FromBody] PaginacaoDto paginacao)
{
// Validação de entrada
if (paginacao.PageSize <= 0 || paginacao.PageSize > 1000)
throw new ValidationException("PageSize deve estar entre 1 e 1000");
if (paginacao.PageIndex < 0)
throw new ValidationException("PageIndex deve ser maior ou igual a 0");
var result = await _service.SincronizarCondicoesPagamentosAsync(paginacao);
return Ok(result);
}
}
Padrões de Resposta
1. Resposta Padrão
public class CondicoesPagamentosResponseDto
{
/// <summary>
/// Lista de itens paginados
/// </summary>
public List<CondicaoPagamentoItemDto> Items { get; set; } = new();
/// <summary>
/// Informações de paginação
/// </summary>
public PaginacaoDto Pagination { get; set; } = new();
}
2. Cálculo de Paginação
Esse código está presente na classe PaginacaoService
// PaginacaoService.cs
private PaginacaoDto CalcularPaginacao(int pageIndex, int pageSize, int resultsCount)
{
return new PaginacaoDto
{
PageIndex = pageIndex,
PageSize = pageSize,
HasNextPage = resultsCount > pageSize
};
}
Tratamento de Erros
1. Exemplo de utilização com o FluentValidator
Esse exemplo pode ser ignorado por enquanto, a biblioteca de FluentValidation será implementada no futuro, pois ainda não tive tempo de implementar no código atual. 041537 - [WSConsultas] Implementar biblioteca FluentValidation para validações dinâmicas
public class PaginacaoDtoValidator : AbstractValidator<PaginacaoDto>
{
public PaginacaoDtoValidator()
{
RuleFor(x => x.PageSize)
.InclusiveBetween(1, 1000)
.WithMessage("PageSize deve estar entre 1 e 1000");
RuleFor(x => x.PageIndex)
.GreaterThanOrEqualTo(0)
.WithMessage("PageIndex deve ser maior ou igual a 0");
}
}
2. Tratamento de Exceções
public async Task<CondicoesPagamentosResponseDto> SincronizarCondicoesPagamentosAsync(
PaginacaoDto paginacao)
{
try
{
// ... implementação
}
catch (Exception ex)
{
_logger.LogError(ex, "Erro ao sincronizar condições de pagamento");
throw new ValidationException("Erro interno ao processar paginação");
}
}
Boas Práticas
1. Performance
- Use
AsNoTracking()para consultas somente leitura - Aplique índices no banco de dados
- Use projeção para selecionar apenas campos necessários
- Implemente cache para contagens frequentes
2. Segurança
- Valide sempre os parâmetros de entrada
- Limite o tamanho máximo da página
- Implemente rate limiting para endpoints de paginação
3. Manutenibilidade
- Separe a lógica de paginação em métodos reutilizáveis
- Use DTOs específicos para filtros
- Documente os parâmetros de entrada e saída