Alura > Cursos de Programação > Cursos de .NET > Conteúdos de .NET > Primeiras aulas do curso .NET 6: relacionando entidades

.NET 6: relacionando entidades

Crescendo o projeto - Apresentação

Olá! Sou o Daniel e serei seu instrutor nesse curso de relacionamento com entidades .NET e Entity.

Daniel Artine é uma pessoa de pele clara, olhos castanhos escuros e cabelos pretos curtos. Usa barba e bigode. Está com camiseta preta, sentado em uma cadeira preta. Ao fundo, há uma parede com iluminação azul.

Neste curso, começaremos exatamente de onde paramos no curso anterior, vamos crescer o nosso projeto. Além de fazer a inserção de filmes, vamos também inserir cinemas, endereços e sessões.

Nosso sistema ficará mais complexo e completo.

Agora precisaremos relacionar os conceitos. Como um filme vai se relacionar com o cinema dentro do nosso sistema? Ou como o endereço do cinema pode ser recuperado através de uma consulta?

Faremos criações e mudanças através de migrations. Já vimos esse conceito no curso anterior e veremos mais nesse curso, pois vamos gerar novas migrations para alterar nosso banco de dados.

E faremos a construção dos modelos:

Faremos também os devidos mapeamentos com o AutoMapper, usando as mesmas ferramentas do curso anterior.

Pré-requisitos

Antes de começarmos, é muito importante que você tenha feito os cursos de pré-requisito:

HTTP: Entendendo a web por baixo dos panos

.NET 6: criando uma web API

O nosso ponto de partida será o projeto final do curso anterior. Caso você tenha feito esse projeto e queira seguir com ele, certifique-se de que ele está bem alinhado com a forma como terminamos o curso anterior para evitar incompatibilidades.

Espero que você aproveite esse curso e saia com novos conhecimentos. No próximo vídeo começaremos com nosso conteúdo. Até lá!

Crescendo o projeto - Adicionando o Cinema

Neste curso queremos tornar nosso projeto ainda maior, no conceito de termos diferentes entidades que vão se relacionar.

Para isso, o primeiro passo será criar uma dessas novas entidades. Começaremos nosso projeto exatamente de onde paramos no curso anterior, então já temos a entidade de filmes. Recomendo que você faça a partir desse projeto que está mais alinhado com o fluxo que usaremos aqui.

O primeiro passo será criar a classe Cinema e os nossos conceitos de cinema que, futuramente, vão se relacionar com a entidade de filmes.

Esse vídeo será uma espécie de revisão sobre criação de:

A princípio, vamos criar o modelo de Cinema. No painel do gerenciador de soluções clicaremos com o botão direto sobre a pasta "Models" e selecionaremos "Adicionar > Classe...". Criaremos uma nova classe chamada Cinema.cs.

Neste momento, o que precisamos no código de Cinema.cs é um identificador, dentro do escopo de public class Cinema vamos inserir [Key]. Com o Key selecionado usaremos o atalho "Alt + Enter" para importar o namespace necessário, que é o de DataAnnotations.

Teremos também a chave Required e criaremos um public int Id, que é o identificador dessa entidade dentro do banco de dados.

using System.ComponentModel.DataAnnotations;

namespace FilmesApi.Models
{
    public class Cinema
    {
        [Key]
        [Required]
        public int Id { get; set; }

    }
}

Por enquanto, nosso atributo terá mais um item como Required, o nome, ou seja, qual o nome do cinema que será criado?

using System.ComponentModel.DataAnnotations;

namespace FilmesApi.Models
{
    public class Cinema
    {
        [Key]
        [Required]
        public int Id { get; set; }
        [Required]
        public string Nome { get; set; }

    }
}

Para tornar o nosso modelo um pouco mais robusto vamos complementar o Required do campo de nome definindo uma mensagem de erro, informaremos que o nome é obrigatório:

[Required(ErrorMessage = "O campo de nome é obrigatório.")]

Cinema.cs

using System.ComponentModel.DataAnnotations;

namespace FilmesApi.Models
{
    public class Cinema
    {
        [Key]
        [Required]
        public int Id { get; set; }
        [Required(ErrorMessage = "O campo de nome é obrigatório.")]
        public string Nome { get; set; }

    }
}

DTOs

Já criamos o modelo de Cinema, agora criaremos os DTOs.

No painel do gerenciador de soluções, clicaremos com o botão direto sobre a pasta "Dtos" e selecionaremos a opção "Adicionar > Classe...". Criaremos uma nova classe chamada CreateCinemaDto.cs.

Para criar um cinema qual é o campo que precisamos passar? Nosso usuário precisará se preocupar em passar ID porque ainda estamos criando esse recurso, então só precisamos nos preocupar com a criação de um nome. Vamos inserir as linhas que dizem respeito ao nome do cinema para o código do arquivo CreateCinemaDto, as linhas de [Required] e a de public string Nome.

using System.ComponentModel.DataAnnotations;

namespace FilmesApi.Data.Dtos
{
    public class CreateCinemaDto
    {
        [Required(ErrorMessage = "O campo de nome é obrigatório.")]
        public string Nome { get; set; }
    }
}

Em seguida, criaremos também o DTO de leitura.

No painel do gerenciador de soluções, clicaremos com o botão direto sobre a pasta "Dtos" e selecionaremos a opção "Adicionar > Classe...". Criaremos uma nova classe chamada ReadCinemaDto. E dentro desse arquivo teremos esses dois parâmetros, duas propriedades que queremos retornar: ID e nome.

namespace FilmesApi.Data.Dtos
{
    public class ReadCinemaDto
    {
        public int Id { get; set; }
        public string Nome { get; set; }
    }
}

Agora, podemos adicionar outra nova classe dentro da pasta "Dtos". Nomearemos essa classe de UpdateCinemaDto. O arquivo UpdateCinemaDto.cs terá o parâmetro de Nome. Vamos receber o ID através da URL e definiremos qual será o nome do cinema.

using System.ComponentModel.DataAnnotations;

namespace FilmesApi.Data.Dtos
{
    public class UpdateCinemaDto
    {
        [Required(ErrorMessage = "O campo de nome é obrigatório.")]
        public string Nome { get; set; }
    }
}

Controller

Já criamos o nosso modelo e os DTOs. Agora criaremos o controlador.

No painel do gerenciador de soluções, clicaremos com o botão direto sobre a pasta "Controllers" e selecionaremos a opção "Adicionar > Classe...". Nomearemos essa classe de CinemaController.

Lembrando que já estudamos esses procedimentos com mais profundidade no curso anterior. Caso você não esteja entendendo, é sinal de que você deve fazer o curso anterior que é pré-requisito para este curso.

Então, como o controlador é uma classe que terá um bloco de código um pouco maior, vou colar um código pronto que eu tenho aqui para seguirmos mais rapidamente. De qualquer forma, passaremos por ele para dar uma revisada.

CinemaController.cs

using AutoMapper;
using FilmesApi.Data.Dtos;
using FilmesApi.Data;
using FilmesApi.Models;
using Microsoft.AspNetCore.Mvc;

namespace FilmesApi.Controllers
{
    [ApiController]
    [Route("[controller]")]

    public class CinemaController : ControllerBase
    {
        private FilmeContext _context;
        private IMapper _mapper;

        public CinemaController(FilmeContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }


        [HttpPost]
        public IActionResult AdicionaCinema([FromBody] CreateCinemaDto cinemaDto)
        {
            Cinema cinema = _mapper.Map<Cinema>(cinemaDto);
            _context.Cinemas.Add(cinema);
            _context.SaveChanges();
            return CreatedAtAction(nameof(RecuperaCinemasPorId), new { Id = cinema.Id }, cinemaDto);
        }

        [HttpGet]
        public IEnumerable<ReadCinemaDto> RecuperaCinemas()
        {
            return _mapper.Map<List<ReadCinemaDto>>(_context.Cinemas.ToList());
        }

        [HttpGet("{id}")]
        public IActionResult RecuperaCinemasPorId(int id)
        {
            Cinema cinema = _context.Cinemas.FirstOrDefault(cinema => cinema.Id == id);
            if (cinema != null)
            {
                ReadCinemaDto cinemaDto = _mapper.Map<ReadCinemaDto>(cinema);
                return Ok(cinemaDto);
            }
            return NotFound();
        }

        [HttpPut("{id}")]
        public IActionResult AtualizaCinema(int id, [FromBody] UpdateCinemaDto cinemaDto)
        {
            Cinema cinema = _context.Cinemas.FirstOrDefault(cinema => cinema.Id == id);
            if (cinema == null)
            {
                return NotFound();
            }
            _mapper.Map(cinemaDto, cinema);
            _context.SaveChanges();
            return NoContent();
        }


        [HttpDelete("{id}")]
        public IActionResult DeletaCinema(int id)
        {
            Cinema cinema = _context.Cinemas.FirstOrDefault(cinema => cinema.Id == id);
            if (cinema == null)
            {
                return NotFound();
            }
            _context.Remove(cinema);
            _context.SaveChanges();
            return NoContent();
        }

    }
}

Neste código que colamos em CinemaController.cs, estamos definindo as anotações de controlador com [ApiController] e nossa rota para /cinema que é o nome do nosso controlador, [Route("[controller]")].

Em seguida, estendemos o ControllerBase, fizemos a injeção das dependências, que no caso serão FilmeContext e IMapper.

Depois, temos as operações básicas. O bloco do [HttpPost] para adicionar um cinema. Neste bloco de código nós recebemos o CreateCinemaDto, mapeamos ele para um Cinema, adicionamos ele no nosso context– Então, precisamos ainda criar o DbSet, que é nosso próximo passo – Salvamos as alterações com SaveChanges() e retornamos à rota em que ele foi criado.

Em seguida, no bloco de código de [HttpGet] usamos o RecuperaCinemas(), retornamos todos os cinemas que temos cadastrados.

E temos também o GET por ID, [HttpGet("{id}")]. Em que recebemos um ID e retornamos o cinema correspondente.

Para atualização inserimos o PUT por ID, [HttpPut("{id}")].

E, por fim, temos o bloco do DELETE por ID, [HttpDelete("{id}")].

Agora precisamos criar o DbSet. Para criá-lo vamos para o início do código de CinemaController e, segurando o "Ctrl", clicaremos em "FilmeContext" para acessar o arquivo FilmeContext.cs.

No FilmeContext.cs criaremos o DbSet de Cinema, DbSet<Cinema> Cinemas { get; set; }:

public class FilmeContext : DbContext
{
    public FilmeContext(DbContextOptions<FilmeContext> opts)
        : base(opts)
    {

    }

    public DbSet<Filme> Filmes { get; set; }
    public DbSet<Cinema> Cinemas { get; set; }
}

Agora, um último detalhe. Tem um erro silencioso que aconteceria caso executássemos o nosso sistema. Porque ainda não ensinamos o AutoMapper a fazer esse mapeamento Cinema cinema = _mapper.Map<Cinema>(cinemaDto).

Para fazer isso, criaremos um profile. Na pasta "Profiles", vamos criar uma nova classe chamada CinemaProfile.

Profile

Para ser efetivamente um profile precisamos estender da classe Profile e para criar o construtor usaremos o atalho de escrever "ctor" e pressionar "Tab" duas vezes. Usaremos o método CreateMap para criar o Map de CreateCinemaDto para Cinema e faremos a mesma coisa para os outros DTOs, na segunda linha, faremos de um Cinema para um ReadCinemaDto e, na terceira linha, de um UpdateCinemaDto para um Cinema.

using AutoMapper;
using FilmesApi.Data.Dtos;
using FilmesApi.Models;

namespace FilmesApi.Profiles
{
    public class CinemaProfile : Profile
    {
        public CinemaProfile()
        {
            CreateMap<CreateCinemaDto, Cinema>();
            CreateMap<Cinema, ReadCinemaDto>();
            CreateMap<UpdateCinemaDto, Cinema>();
        }
    }
}

Agora, ao analisar o CinemaControllerestamos, a princípio, sem nenhum problema.

Então, neste vídeo o que fizemos foi uma breve revisão desses conceitos mais importantes que vimos anteriormente. Seguiremos aplicando-os em alguns cenários.

Agora temos um conceito de cinema. Conseguimos cadastrar cinemas e fazer operações com cinemas dentro do nosso sistema.

Agora precisamos pensar: como o Cinema vai se relacionar com outras classes dentro do nosso banco?

Te espero no próximo vídeo!

Crescendo o projeto - Apresentando o problema

Agora vamos discutir um pouco de teoria para entender qual é o problema a ser resolvido.

Qual situação queremos melhorar?

Temos, por enquanto, a classe Filme, a classe Cinema com seus respectivos DTOs, controladores e profile. Ainda não temos a classe Endereço, mas em breve vamos criá-la.

Diagrama representando conexão entre tabelas e banco de dados. À esquerda três retângulos dispostos verticalmente representam respectivamente: Filme, Cinema e Endereço. De cada retângulo sai uma seta que aponta para o ícone de Nanco de Dados localizado à direita.
Ao lado de cada retângulo a informação que eles guardam. tabela Filme possui "Título: Senhor dos Anéis" e "Lançamento: 2001"; tabela Cinema possui "Nome: Alura Cinema"; tabela Endereço possui "Logradouro: Rua qualquer" e "Bairro: reticências".

O ponto aqui é o seguinte: nós temos algumas maneiras de representar esses dados. Podemos ter um filme com título "O senhor dos Anéis" e ano de lançamento "2001"; um cinema com nome "Alura Cinema", por enquanto é o único campo que temos; um endereço com dados de logradouro e bairro.

Temos maneiras de representar esses dados e sabemos que jogaremos tudo isso dentro do nosso banco de dados. Mas onde queremos chegar?

No momento em que fazemos alguma operação como, por exemplo, cadastrar um cinema ou pegar uma informação desse cinema, na visão do usuário final podemos pensar na pergunta: eu sei que o cinema existe, mas qual é o endereço desse cinema?

Anteriormente, vimos que a ideia é relacionar, por exemplo, um cinema com um filme, mas temos que pensar também em conceitos mais simples. Por exemplo: cinema e endereço.

Poderíamos criar uma coluna de endereço para cada cinema e colocar essa informação lá.

É uma opção válida, mas vamos pensar que podemos querer usar esse mesmo endereço em outros campos, outras informações que tenham esse mesmo endereço e queremos armazenar endereço para diversos tipos de informações diferentes.

Então, faz sentido ter essa informação em uma tabela.

A partir de agora a ideia é que tenhamos um cinema e tenhamos também uma relação com o endereço, que ainda vamos criar.

Diagrama representando a conexão entre tabela Cinema e tabela Endereço no banco de dados. Cinema, Endereço e Banco de Dados conectados por uma seta em formato triangular.

Então, esse endereço vai ter informações como logradouro e bairro, e o cinema terá esse endereço.

Como isso funcionará dentro da visão de cliente, servidor e banco de dados?

No momento em que fizermos uma operação de recuperar um cinema por ID, GET /cinema/{id}, vamos trazer a informação desse cinema e, junto com essa informação, teremos também qual é o endereço desse cinema.

Então, a partir desse momento conseguimos fazer essa operação. Precisamos ver como fazer esse relacionamento entre a tabela de cinema e a tabela de endereço que estão no nosso banco de dados.

Mas, como vamos trazer essa informação? Como saberemos que o endereço do cinema está correto? Como juntamos essas informações? Responderemos isso em breve.

Mas temos que resolver algumas perguntas antes.

Diagrama de comunicação de dados entre cliente, servidor e banco de dados. Da esquerda para a direita temos três ícones: os dois primeiros são ícones de computadores, o primeiro representa "Cliente" e o segundo representa "Servidor", o terceiro é um ícone de banco de dados acompanhado do texto "Banco de dados" na parte inferior. À direita do computador que representa "Cliente" temos o texto: "POST /cinema" e uma lista com dois itens, o primeiro item com texto "Nome: Alura Cinema" e o segundo item com pontos de reticências.

Respondendo à primeira questão, pensando de maneira física: não. Se um cinema está em algum lugar, ele precisa ter um endereço.

E um endereço pode existir sem um cinema? Pode. Note que estamos criando uma relação de dependência.

Então qual é a classe que tem mais importância nesse cenário? Seria o endereço.

Adiante vamos entender como criar todo o conceito de endereço, que será parecido com o que fizemos com cinema. Mas como criaremos e relacionaremos os endereços aos cinemas.

Sobre o curso .NET 6: relacionando entidades

O curso .NET 6: relacionando entidades possui 125 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de .NET em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda .NET acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas