Atualização com PUT e upsert
Resumo
PUTsubstitui integralmente um recurso e é idempotente. Aproveitando o change tracking do EF Core, a atualização se torna trivial. Vemos também o padrão upsert (inserir ao atualizar quando o recurso não existe).
1. Objetivos de Aprendizagem#
- Implementar
PUTretornando204 No Content. - Usar
trackChanges: truepara persistir alterações sem chamarUpdateexplicitamente. - Entender o padrão upsert.
2. Pré-requisitos#
- Criação com
POST(Módulo 23).
3. Conceito#
PUT representa a substituição completa do recurso: o cliente envia o estado desejado inteiro. É idempotente — enviar o mesmo corpo N vezes resulta no mesmo estado. O porquê de buscar com trackChanges: true: ao carregar a entidade rastreada, aplicar o mapeamento e chamar SaveChangesAsync, o EF Core detecta as mudanças e gera o UPDATE automaticamente.
4. Mão na Massa#
4.1. Setup#
DTO de atualização e mapeamento correspondente.
4.2. Implementação Passo a Passo#
- DTO:
public record ProdutoForUpdateDto(string Nome, decimal Preco);
- Serviço (atualização por tracking):
public async Task UpdateAsync(Guid categoriaId, Guid id, ProdutoForUpdateDto dto)
{
_ = await _repo.Categoria.GetByIdAsync(categoriaId, trackChanges: false)
?? throw new CategoriaNotFoundException(categoriaId);
var produto = await _repo.Produto.GetByIdAsync(categoriaId, id, trackChanges: true)
?? throw new ProdutoNotFoundException(id);
_mapper.Map(dto, produto); // aplica sobre a entidade rastreada
await _repo.SaveAsync(); // EF detecta mudanças e gera UPDATE
}
- Controller:
[HttpPut("{id:guid}")]
public async Task<IActionResult> Atualizar(Guid categoriaId, Guid id, [FromBody] ProdutoForUpdateDto dto)
{
await _service.Produto.UpdateAsync(categoriaId, id, dto);
return NoContent();
}
4.3. Executando#
curl -i -X PUT http://localhost:5000/api/categorias/{catId}/produtos/{id} \
-H "Content-Type: application/json" \
-d '{"nome":"Mouse Pro","preco":149.9}'
# HTTP/1.1 204 No Content
5. Exemplo Completo#
Upsert: se o recurso não existe, em vez de 404, criamos um novo com o id informado. Útil quando o cliente controla o id. Use com cuidado, pois muda a semântica de PUT.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Buscar com trackChanges: true para atualizar | Buscar sem tracking e depois estranhar que nada foi salvo |
Enviar o recurso completo no PUT | Usar PUT para atualização parcial (use PATCH) |
Retornar 204 em sucesso | Retornar o recurso inteiro sem necessidade |
Atençãoatualização parcial via
PUT(enviar só alguns campos) zera os campos omitidos, poisPUTsubstitui tudo. Para atualizações parciais, usePATCH(Módulo 26).
7. Segurança e Produção#
- Controle concorrência com um concurrency token (
rowversion) para evitar lost updates quando dois clientes editam o mesmo recurso.
8. Exercícios#
- Fácil: implemente
PUTde categoria. - Médio: adicione um
rowversione trateDbUpdateConcurrencyExceptionretornando409 Conflict. - Desafio: implemente upsert e discuta seus riscos.
9. Resumo#
PUT substitui o recurso e é idempotente. Com change tracking, a atualização é automática ao salvar. Concorrência otimista protege contra sobrescritas perdidas.
10. Próximos Passos#
Módulo 26: atualização parcial com PATCH e JSON Patch.
11. Referências#
- Microsoft Learn — Atualizar dados no EF Core
- Microsoft Learn — Concorrência otimista
- MDN — PUT