Refatorando repositório, serviço e controller
ResumoConvertemos 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
CancellationTokendo 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#
- Interface do repositório:
public interface ICategoriaRepository
{
Task<IEnumerable<Categoria>> GetAllAsync(bool trackChanges, CancellationToken ct = default);
Task<Categoria?> GetByIdAsync(Guid id, bool trackChanges, CancellationToken ct = default);
}
- Implementação:
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);
- Manager:
SaveAsyncjá assíncrono. - Service Layer: cada método
async Task<...>. - Controller: cada ação
async Task<IActionResult>, recebendoCancellationToken:
[HttpGet]
public async Task<IActionResult> Get(CancellationToken ct)
{
var categorias = await _service.Categoria.GetAllAsync(trackChanges: false, ct);
return Ok(categorias);
}
4.3. Executando#
dotnet build && dotnet run
Funcionalmente idêntico, porém escalável e cancelável.
5. Exemplo Completo#
Assinatura coerente em todas as camadas:
Controller.Get(CancellationToken)
→ Service.GetAllAsync(trackChanges, ct)
→ Repository.GetAllAsync(trackChanges, ct)
→ EF Core ToListAsync(ct)
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Assinaturas async coerentes em toda a pilha | Bloquear com .Result/.Wait() |
Propagar CancellationToken até o EF Core | Aceitar o token e não repassá-lo |
Sufixar métodos assíncronos com Async | Assinaturas ambíguas entre sync e async |
Atençãoaceitar
CancellationTokenmas não repassá-lo aoToListAsyncé 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
CancellationTokena mais um endpoint. - Médio: refatore o serviço de produtos inteiro para async com token.
- Desafio: demonstre um deadlock causado por
.Resulte 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#
- Microsoft Learn — Async no ASP.NET Core
- Microsoft Learn — CancellationToken em ações
- Microsoft Learn — EF Core assíncrono