Pular para o conteúdo
.NETRefatorando repositório, serviço e controller
Módulo 04CRUD Completo e Validação

Refatorando repositório, serviço e controller

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

Convertemos a pilha inteira para assíncrona de ponta a ponta: interface do repositório, implementação, manager, service layer e controller, propagando CancellationToken.

1. Objetivos de Aprendizagem#

  • Refatorar interfaces e implementações para async.
  • Manter a coerência de assinatura em todas as camadas.
  • Propagar CancellationToken do controller ao EF Core.

2. Pré-requisitos#

  • async/await (tópico 28.1).

3. Conceito#

Assincronismo "vaza" pelas camadas: se o repositório é async, o serviço que o consome também deve ser, e assim por diante até o controller. O porquê: bloquear com .Result/.Wait() no meio anula o benefício e pode causar deadlocks. Portanto, refatoramos de forma consistente.

4. Mão na Massa#

4.1. Setup#

Sem pacotes novos.

4.2. Implementação Passo a Passo#

  1. Interface do repositório:
C#
public interface ICategoriaRepository
{
    Task<IEnumerable<Categoria>> GetAllAsync(bool trackChanges, CancellationToken ct = default);
    Task<Categoria?> GetByIdAsync(Guid id, bool trackChanges, CancellationToken ct = default);
}
  1. Implementação:
C#
public async Task<IEnumerable<Categoria>> GetAllAsync(bool trackChanges, CancellationToken ct = default) =>
    await FindAll(trackChanges).OrderBy(c => c.Nome).ToListAsync(ct);

public async Task<Categoria?> GetByIdAsync(Guid id, bool trackChanges, CancellationToken ct = default) =>
    await FindByCondition(c => c.Id == id, trackChanges).SingleOrDefaultAsync(ct);
  1. Manager: SaveAsync já assíncrono.
  2. Service Layer: cada método async Task<...>.
  3. Controller: cada ação async Task<IActionResult>, recebendo CancellationToken:
C#
[HttpGet]
public async Task<IActionResult> Get(CancellationToken ct)
{
    var categorias = await _service.Categoria.GetAllAsync(trackChanges: false, ct);
    return Ok(categorias);
}

4.3. Executando#

Shell
dotnet build && dotnet run

Funcionalmente idêntico, porém escalável e cancelável.

5. Exemplo Completo#

Assinatura coerente em todas as camadas:

Code
Controller.Get(CancellationToken)
  → Service.GetAllAsync(trackChanges, ct)
    → Repository.GetAllAsync(trackChanges, ct)
      → EF Core ToListAsync(ct)

6. Boas Práticas e Armadilhas#

FaçaEvite
Assinaturas async coerentes em toda a pilhaBloquear com .Result/.Wait()
Propagar CancellationToken até o EF CoreAceitar o token e não repassá-lo
Sufixar métodos assíncronos com AsyncAssinaturas ambíguas entre sync e async
Atenção

aceitar CancellationToken mas não repassá-lo ao ToListAsync é inútil — o cancelamento nunca chega ao banco.

7. Segurança e Produção#

  • Cancelamento propagado reduz carga sob picos e ataques de slow client, liberando conexões de banco mais cedo.

8. Exercícios#

  • Fácil: adicione CancellationToken a mais um endpoint.
  • Médio: refatore o serviço de produtos inteiro para async com token.
  • Desafio: demonstre um deadlock causado por .Result e corrija-o.

9. Resumo#

Refatoramos toda a pilha para assíncrona e cancelável, com assinaturas coerentes. Nada de bloqueio síncrono no meio do caminho.

10. Próximos Passos#

A seguir, as armadilhas mais comuns do código assíncrono.

11. Referências#

28-02 — Refatorando repositório, serviço e controller | Curso ASP.NET Core