EF Core - Definindo os relacionamentos - III
 Neste artigo vamos continuar mostrando como definir os principais relacionamentos entre as entidades usando o EF Core em um projeto prático.


Continuando o artigo anterior vamos agora implementar a funcionalidade para atualizar a Distribuidora.

 


No projeto foi criada uma entidade de junção denominada como FilmeDistribuidora para poder criar o relacionamento Muitos-Para-Muitos entre as entidades 'Filme' e 'Distribuidora' e para poder implementar as funcionalidades relacionadas vamos criar o controlador FilmeDistribuidora na pasta Controllers.

 

Este controlador terá apenas um método Action Update onde vamos definir o relacionamento Muitos para Muitos para as entidades. Para isso iremos inserir vários registros na tabela FilmeDistribuidora simultaneamente para criar esse relacionamento.

 

O link para este método Action foi definido na View Index do controlador FilmesController. Assim, vamos criar o relacionamento para as entidades Filme e Distribuidora  clicando no botão Distribuidora no gerenciamento de filmes.

 

A seguir inclua  código abaixo no controlador FilmeDistribuidoraController:

 

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using XFilmes.Context;
using XFilmes.Models;

namespace XFilmes.Controllers;

public class FilmeDistribuidoraController : Controller
{
    private readonly AppDbContext _context;
    public FilmeDistribuidoraController(AppDbContext db)
    {
        _context = db;
    }

    public IActionResult Update(int id)
    {
        GetFilmeDistribuidora(id);

        var filme = _context.Filmes.Where(a => a.FilmeId == id).FirstOrDefault();
        return View(filme);
    }

    [HttpPost]
    public async Task<IActionResult> Update(Filme filme, string[] distribution)
    {
        GetFilmeDistribuidora(filme.FilmeId);

        if (ModelState.IsValid)
        {
            _context.RemoveRange(_context.FilmesDistribuidoras.Where(t =>
                                               t.FilmeId == filme.FilmeId).ToList());

            List<FilmeDistribuidora> filmesDistribuidoras = new List<FilmeDistribuidora>();

            foreach (string d in distribution)
            {
                var filmedistribuidoraLista = new FilmeDistribuidora()
                {
                    FilmeId = filme.FilmeId,
                    DistribuidoraId = Convert.ToInt32(d)
                };
                filmesDistribuidoras.Add(filmedistribuidoraLista);
            }

            _context.AddRange(filmesDistribuidoras);

            await _context.SaveChangesAsync();

            return RedirectToAction("Index", "Filmes");
        }
        else
            return View(filme);
    }

    void GetFilmeDistribuidora(int filmeId)
    {
        List<SelectListItem> filmeDistribuidora = new List<SelectListItem>();
        filmeDistribuidora = _context.Distribuidoras.Select(x => new SelectListItem
        {
            Text = x.Nome,
              Value = x.DistribuidoraId.ToString(),
              Selected = x.FilmesDistribuidoras.Where(y => y.FilmeId == filmeId)
              .Any(z => z.DistribuidoraId == x.DistribuidoraId)
        }).ToList();

        ViewBag.FilmeDistribuidora = filmeDistribuidora;
    }
}

 

Vamos entender o código :

 

No método Action Update, primeiro todos os registros mais antigos de um filme serão excluídos e, em seguida, novos registros desse filme são inseridos no banco de dados. Isso é feito para garantir que o relacionamento Muitos-para-Muitos permaneça preciso.

Portanto, o código que exclui os registros mais antigos do filme foi definido pelo código:

 _context.RemoveRange(_context.FilmesDistribuidoras
     .Where(t => t.FilmeId == filme.FilmeId).ToList());


Em seguida, os novos registros são inseridos com o método AddRange():

        _context.AddRange(filmesDistribuidoras);

3. A variável chamada “filmesDistribuidora” é do tipo List e seu trabalho é criar uma lista de registros a serem inseridos pelo método AddRange(). Veja o código abaixo que está fazendo a mesma coisa explicada anteriormente:

 List<FilmeDistribuidora> filmesDistribuidoras = new List<FilmeDistribuidora>();
  foreach (string d in distribution)
   {
      var filmedistribuidoraLista = new FilmeDistribuidora()
       {
          FilmeId = filme.FilmeId,
           DistribuidoraId = Convert.ToInt32(d)
        };
        filmesDistribuidoras.Add(filmedistribuidoraLista);
   }

O parâmetro da Action obtém um array de strings contendo todos os ids de distribuidoras e estes são então adicionados à variável pelo laço foreach.

O método GetFilmeDistribuidora() busca os registros de Distribuidora e os adiciona a um objeto List que é enviado para a View em um objeto ViewBag. Um controle de atributo Select “multiple” é vinculado a esses valores.
 

O código da View Update.cshtml que será criada na pasta /Views/FilmeDistribuidora possui o código abaixo:

 

@model Filme

<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>

<script>
    $(document).ready(function () {
        $("select").select2();
    });
</script>

<h3 class="bg-info text-white">Cria uma Distribuidora de um filme</h3>
<a asp-controller="Filmes" asp-action="Index" class="btn btn-secondary">Ver Filmes</a>

<div asp-validation-summary="All" class="text-danger"></div>

<form method="post" enctype="multipart/form-data">
    <div class="form-group">
        <label asp-for="FilmeId"></label>
        <input type="text" asp-for="FilmeId" disabled class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="@Model.Nome"></label>
        <input type="text" asp-for="@Model.Nome" disabled class="form-control" />
    </div>
    <div class="form-group">
        <label for="Distribuidora"></label>
        <select id="Distribuidora" name="Distribuidora" asp-items="ViewBag.FilmeDistribuidora" multiple class="form-control">
        </select>

    </div>
    <div style="padding-top:10px">
     <button type="submit" class="btn btn-primary">Criar</button>
     <button type="submit" class="btn btn-info" asp-action="Index" asp-controller="Filmes">Retornar</button>
    </div>
</form>

 

Neste código estamos recebendo um objeto do tipo Filme como modelo : @model Filme

 

O controle Select é transformado em um plugin jQuery Select2. Assim, os usuários poderão selecionar vários valores de Distribuidora no controle de seleção.  (Veja mais detalhes sobre este plugin aqui : https://select2.org/ )

 

Esse recurso nos ajudaráo na criação do relacionamento Muitos para Muitos. O controle de seleção é vinculado ao valor da variável ViewBag que é enviada pelo controlador:

 <select id="Distribuidora" name="Distribuidora"
    asp-items="ViewBag.FilmeDistribuidora"
    multiple class="form-control">
 </select>

 

Se executarmos o projeto e acionarmos o botão distribuidora neste momento iremos obter o seguinte resultado:

 


Para poder ver o recurso funcionando teremos que implementar o gerenciamento das Distribuidoras onde vamos cadastrar as distribuidoras para isso vamos criar o controlador DistribuidorasController.

 

A distribuidora  vai conter informações da distribuição do filme. A entidade Distribuidora tem um relacionamento de muitos para muitos com a entidade Filme, e, usamos uma entidade de associação chamada FilmeDistribuidora para criar esse relacionamento.

 

Vou implementar apenas os métodos Action Create para criar uma nova distribuidora e o método action Index para exibir as distribuidoras conforme mostra o código a seguir no controlador DistribuidorasController:

 

using Microsoft.AspNetCore.Mvc;
using XFilmes.Context;
using XFilmes.Models;

namespace XFilmes.Controllers;

public class DistribuidorasController : Controller
{
    private AppDbContext context;
    public DistribuidorasController(AppDbContext db)
    {
        context = db;
    }

    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Create(Distribuidora distribuidora)

    {
        if (ModelState.IsValid)
        {
            context.Add(distribuidora);
            await context.SaveChangesAsync();

            return RedirectToAction("Index");
        }
        else
            return View();
    }

     public async Task<IActionResult> Index(int id)
    {
        var distribuidoras = await _context.Distribuidoras.Include(t=>
                        t.FilmesDistribuidoras).ThenInclude(f=> f.Filme).ToListAsync();
        return View(distribuidoras);
    }
}


Note que estamos incluindo as distribuidoras e filmes para cada distribuidora :

 

  var distribuidoras = await _context.Distribuidoras
.
Include(t=> t.FilmesDistribuidoras)
.
ThenInclude(f=> f.Filme).ToListAsync();

Abaixo temos o código da view  Index.cshtml :

 

@model List<Distribuidora>

<h2 class="bg-info text-white">Filmes</h2>

<a asp-action="Create" class="btn btn-secondary">Criar Distribuidora</a>

<table class="table table-sm table-striped">
    <tr>
        <th>Id</th>
        <th>Nome</th>
        <th>Filme</th>
        <th>Local</th>
        <th>Telefone</th>
        <th></th>
        <th></th>
    </tr>
    @foreach (Distribuidora distribuidora in Model)
    {
        <tr>
            <td>@distribuidora.DistribuidoraId</td>
            <td>@distribuidora.Nome</td>
            <td>
                <table>
                    <tr>
                        @foreach (FilmeDistribuidora fd in distribuidora.FilmesDistribuidoras)
                        {
                            <td>
                                @fd.Filme.Nome
                            </td>
                        }

                    </tr>
                </table>
            </td>
            <td>@distribuidora.Local</td>
            <td>@distribuidora.Telefone</td>
            <td>
                <a class="btn btn-sm btn-primary" asp-action="Update"
asp-route-id="@distribuidora.DistribuidoraId">
                    Atualizar
                </a>
            </td>
            <td>
                <form asp-action="Delete"
asp-route-id="@distribuidora.DistribuidoraId" method="post">
                    <button type="submit" class="btn btn-sm btn-danger">
                        Deletar
                    </button>
                </form>
            </td>
        </tr>
    }
</table>


O código da view Create.cshtml é o seguinte:

 

@model Distribuidora

<h2 class="bg-info text-white">Criar uma Distribuidora</h2>
<a asp-action="Index" class="btn btn-secondary">Ver Distribuidoras</a>
 

<div asp-validation-summary="All" class="text-danger"></div>
 

<form method="post">
    <div class="form-group">
        <label asp-for="Nome"></label>
        <input type="text" asp-for="Nome" class="form-control" />
        <span asp-validation-for="Nome" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Local">Local</label>
        <input type="text" asp-for="Local" class="form-control" />
        <span asp-validation-for="Local" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Telefone"></label>
        <input type="text" asp-for="Telefone" class="form-control" />
        <span asp-validation-for="Telefone" class="text-danger"></span>
    </div>
    <div style="padding-top:10px">
       <button type="submit" class="btn btn-primary">Criar</button>
       <button type="submit" class="btn btn-info" asp-action="Index" asp-controller="Distribuidoras">Retornar</button>
    </div>
</form>


Agora podemos atribuir mais de uma Distribuidora a um filme e visualizar o resultado conforme a seguir :

 

1- Exibição das distribuidoras cadastradas e as selecionadas para o Filme:

 

 

2-   Exibição das distribuidoras dos filmes

 

 

Com isso mostramos como definir os principais relacionamentos no EF Core.

 

Pegue o projeto aqui: XFilmes.zip (sem as referências)
 

"Já vos não chamarei servos, porque o servo não sabe o que faz o seu senhor; mas tenho-vos chamado amigos, porque tudo quanto ouvi de meu Pai vos tenho feito conhecer."
João 15:15

 

Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6
Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6

Referências:


José Carlos Macoratti