Pular para o conteúdo
.NETRecursos filhos e coleções
Módulo 04CRUD Completo e Validação

Recursos filhos e coleções

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

Criamos um pai junto com seus filhos numa única requisição e implementamos a criação de uma coleção de recursos, retornando os ids gerados de forma padronizada.

1. Objetivos de Aprendizagem#

  • Criar um recurso pai com filhos aninhados numa só chamada.
  • Implementar criação de coleção (POST de vários itens).
  • Retornar uma coleção criada com Location apropriado.

2. Pré-requisitos#

  • Criação básica com POST (tópico 23.1).

3. Conceito#

Às vezes o cliente quer criar um agregado inteiro (categoria + seus produtos) atomicamente, ou várias entidades de uma vez. O porquê: reduzir round-trips e garantir atomicidade (tudo ou nada, via uma única SaveChangesAsync).

Para coleções, um desafio é o Location: representamos a coleção criada com uma rota que aceita uma lista de ids (ex.: ids=1,2,3), usando um model binder customizado (tópico 23.3).

4. Mão na Massa#

4.1. Setup#

Reuse repositórios e AutoMapper.

4.2. Implementação Passo a Passo#

  1. Criar pai com filhos:
C#
public async Task<CategoriaDto> CreateComProdutosAsync(CategoriaForCreationDto dto)
{
    var categoria = _mapper.Map<Categoria>(dto); // dto inclui a lista de produtos
    _repo.Categoria.Create(categoria);
    await _repo.SaveAsync();                      // salva pai e filhos numa transação
    return _mapper.Map<CategoriaDto>(categoria);
}
  1. Criar coleção de produtos:
C#
public async Task<(IEnumerable<ProdutoDto> produtos, string ids)> CreateColecaoAsync(
    IEnumerable<ProdutoForCreationDto> dtos)
{
    var entidades = _mapper.Map<IEnumerable<Produto>>(dtos).ToList();
    foreach (var e in entidades) _repo.Produto.Create(e);
    await _repo.SaveAsync();

    var result = _mapper.Map<IEnumerable<ProdutoDto>>(entidades);
    var ids = string.Join(',', entidades.Select(e => e.Id));
    return (result, ids);
}
  1. Controller da coleção:
C#
[HttpPost("colecao")]
public async Task<IActionResult> CriarColecao([FromBody] IEnumerable<ProdutoForCreationDto> dtos)
{
    var (produtos, ids) = await _service.Produto.CreateColecaoAsync(dtos);
    return CreatedAtRoute("ProdutoCollection", new { ids }, produtos);
}

4.3. Executando#

Shell
curl -i -X POST http://localhost:5000/api/produtos/colecao \
  -H "Content-Type: application/json" \
  -d '[{"nome":"Mouse","preco":99.9},{"nome":"Teclado","preco":199.9}]'

5. Exemplo Completo#

O endpoint GET /api/produtos/colecao/{ids} (tópico 23.3) usa um binder customizado para transformar 1,2,3 em uma lista de GUIDs, fechando o ciclo com o Location retornado aqui.

6. Boas Práticas e Armadilhas#

FaçaEvite
Salvar o agregado numa única transaçãoSalvar filhos separadamente e arriscar estado parcial
Rejeitar payload nulo de coleção com 400Iterar sobre uma coleção nula
Retornar os ids criados de forma consultávelNão indicar como buscar os recursos criados
Atenção

criação em lote sem limite pode ser abusada. Imponha um tamanho máximo de coleção e valide cada item (Módulo 27).

7. Segurança e Produção#

  • Operações em lote devem ser transacionais e limitadas em tamanho para evitar exaustão de recursos.

8. Exercícios#

  • Fácil: valide que a coleção não é nula nem vazia.
  • Médio: limite a criação a no máximo 100 itens por requisição.
  • Desafio: torne a criação do agregado resiliente, retornando quais itens falharam na validação.

9. Resumo#

Criamos agregados pai+filhos atomicamente e coleções de recursos, retornando ids consultáveis. A atomicidade vem de uma única SaveChangesAsync.

10. Próximos Passos#

A seguir, o model binding que permite receber ids=1,2,3 como coleção tipada.

11. Referências#

23-02 — Recursos filhos e coleções | Curso ASP.NET Core