Pular para o conteúdo principal

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