ASP .NET MVC 5 - Cenário Mestre-Detalhes (Relacionamento Muitos-para-Muitos)
Neste artigo vou mostrar como podemos exibir páginas mestre-detalhes em um cenário onde temos um relacionamento muitos-para-muitos em uma aplicação ASP .NET MVC 5. |
Você vai
encontrar centenas de artigos e vídeos na internet que abordam como criar
aplicações ASP .NET MVC usando o Scaffolding, onde você cria os controladores e
as views de forma automática. Dessa forma, à primeira vista fica a impressão que
podemos gerar qualquer tipo de aplicação usando esses recursos.
Mas no mundo real as coisas não são tão simples e em muitos cenários você vai
ter que ir além do que a ASP .NET MVC e o Scaffolding lhe oferece para poder
criar uma aplicação funcional.
Um desses casos é quando temos que tratar com o relacionamento muitos-para-muitos, para este cenário o Scaffolding não funciona e você vai ter que arregaçar as mangas e por em prática os seus conhecimentos para poder estender os recursos da ASP .NET MVC.
Neste artigo eu vou mostrar com fazer isso na prática partindo do seguinte cenário:
- Desejamos
criar um projeto que vai exibir informações de estudantes e cursos;
- Teremos assim uma página para exibir, criar, deletar e alterar dados dos
estudantes e uma página para fazer a mesma coisa com os cursos;
Até aqui tudo normal, e, até este ponto podemos usar o recurso do Scaffolding para gerar os controladores e views para cursos e estudantes.
O problema é que queremos registrar os cursos nos quais os estudantes estão matriculados, e, então teremos um página para exibir os estudantes e os cursos onde devemos permitir que o estudante selecione os cursos que deseja fazer.
Assim, o estudante deverá poder selecionar e salvar as informações para os cursos nos quais deseja se registrar.
Para concluir devemos exibir em uma página de detalhes do estudante os dados do mesmo e os cursos nos quais ele esta matriculado.
Com essas definições vamos à pratica...
Recursos usados:
Criando o projeto no VS Community
Abra o VS Community 2017 e clique em New Project;
A seguir selecione Visual C# -> Web -> ASP .NET Web Application(.NET Framework)
Informe o nome Mvc_CodeFirst_MuitosMuitos e clique no botão OK;
Selecione o template MVC sem autenticação, conforme figura a seguir:
Ao final da operação a solução criada com toda estrutura pronto onde iremos fazer os ajustes e a implementação do nosso projeto.
Incluindo a referência ao Entity Framework, definindo o modelo de entidades e o contexto
Vamos incluir a referência ao Entity Framework no projeto via Nuget. No menu Tools clique em Nuget Package Manager e a seguir em Manage Nuget Packages for Solution;
Selecione o pacote EntityFramework e marque para instalar no projeto:
Após isso vamos criar as classes das entidades que serão mapeadas pelo EF6 para as tabelas do banco de dados.
Na pasta Models crie o arquivo Curso.cs e a seguir defina o código abaixo na classe Curso:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
[Table("Cursos")]
public class Curso
{
public Curso()
{
this.Estudantes = new HashSet<Estudante>();
}
[Key]
public int CursoId { get; set; }
public string Nome { get; set; }
public Nullable<int> Creditos { get; set; }
public virtual ICollection<Estudante> Estudantes { get; set; }
}
}
|
Curso.cs |
A seguir crie o arquivo Estudante.cs e defina o código da classe Estudante:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
[Table("Estudantes")]
public class Estudante
{
public Estudante()
{
this.Cursos = new HashSet<Curso>();
}
[Key]
public int EstudanteId { get; set; }
public string Nome { get; set; }
public Nullable<int> Idade { get; set; }
public string Sexo { get; set; }
public virtual ICollection<Curso> Cursos { get; set; }
}
}
|
Estudante.cs |
Agora vamos definir a classe que representa o contexto da nossa aplicação. Ainda na pasta Models crie o arquivo dbContexto.cs e defina o código abaixo na classe dbContexto:
using System.Data.Entity;
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
public class dbContexto : DbContext
{
public dbContexto()
: base("name=dbContexto")
{}
public virtual DbSet<Curso> Cursos { get; set; }
public virtual DbSet<Estudante> Estudantes { get; set; }
}
}
|
dbContexto.cs |
Pronto ! Definimos as classes Curso, Estudante e dbContexto e já estamos prontos para criar os controladores e as views da nossa aplicação.
Só falta definir a string de conexão com o banco de dados SQL Server. Abra o arquivo Web.Config e defina a string de conexão para um banco de dados no Sql Server.
Abaixo vemos um exemplo de conexão usando o nome do nosso contexto, dbcontexto, e apontando para o banco EscolaDemo:
....
<connectionStrings> |
Até aqui temos o caminho feliz e podemos gerar os controladores EstudantesController e CursosController na pasta Controllers conforme usando o Scaffolding na opção MVC 5 Controller and views, using Entity Framework, conforme as opção mostradas abaixo:
Ao final teremos os controladores e as views e executando o projeto poderemos acessar as páginas para gerenciar estudantes e cursos.
Mas não temos a opção em nenhuma das páginas geradas para poder selecionar os cursos para um estudante nem exibir os cursos nos quais um estudante esta matriculado.
Vamos então resolver esse problema...
Resolvendo o problema usando ViewModels
Para contornar o problema vamos criar duas ViewModels na pasta Models :
Na pasta Models crie o arquivo CheckBoxViewModel.cs e defina o código abaixo na classe CheckBoxViewModel:
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
public class CheckBoxViewModel
{
public int Id { get; set; }
public string Nome { get; set; }
public bool Checked { get; set; }
}
}
|
Ainda na pasta Models crie o arquivo EstudantesViewModel.cs e defina o código abaixo na classe EstudantesViewModel:
using System;
using System.Collections.Generic;
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
public class EstudantesViewModel
{
public int EstudanteId { get; set; }
public string Nome { get; set; }
public Nullable<int> Idade { get; set; }
public string Sexo { get; set; }
public List<CheckBoxViewModel> Cursos { get; set; }
}
}
|
Definindo a Classe CursoEstudante no modelo e no contexto
Agora precisamos definir a classe CursoEstudante para podermos mapear para a tabela CursosEstudantes que foi gerada:
Na pasta Models crie o arquivo CursoEstudante.cs e define o código abaixo na classe CursoEstudante:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
[Table("CursosEstudantes")]
public class CursoEstudante
{
[Key]
public int CursoEstudanteId { get; set; }
public int CursoId { get; set; }
public int EstudanteId { get; set; }
public virtual Curso Curso { get; set; }
public virtual Estudante Estudante { get; set; }
}
}
|
E finalmente temos que definir manualmente esta classe no nosso contexto. Abra o arquivo dbContexto.cs e inclua a linha de código em azul:
using System.Data.Entity;
namespace Mvc_CodeFirst_MuitosMuitos.Models
{
public class dbContexto : DbContext
{
public dbContexto()
: base("name=dbContexto")
{}
public virtual DbSet<Curso> Cursos { get; set; }
public virtual DbSet<Estudante> Estudantes { get; set; }
public virtual DbSet<CursoEstudante> CursosEstudantes { get; set; }
}
}
|
Agora podemos alterar o controlador EstudantesController para permitir a edição dos cursos e a exibição dos detalhes dos estudantes incluindo agora os cursos.
Ajustando o controlador EstudantesController : métodos Edit e Details
Abra o controlador EstudantesController e altere o código do método Edit/Get conforme mostrado no código destacado em azul abaixo:
// GET: Estudantes/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Estudante estudante = db.Estudantes.Find(id);
if (estudante == null)
{
return HttpNotFound();
}
var CursosEstudantes = from c in db.Cursos
select new
{
c.CursoId,
c.Nome,
Checked = ((from ce in db.CursosEstudantes
where (ce.EstudanteId == id) & (ce.CursoId == c.CursoId)
select ce).Count() > 0)
};
var estudanteViewModel = new EstudantesViewModel();
estudanteViewModel.EstudanteId = id.Value;
estudanteViewModel.Nome = estudante.Nome;
estudanteViewModel.Idade = estudante.Idade;
estudanteViewModel.Sexo = estudante.Sexo;
var checkboxListCursos = new List<CheckBoxViewModel>();
foreach (var item in CursosEstudantes)
{
checkboxListCursos.Add(new CheckBoxViewModel { Id = item.CursoId, Nome = item.Nome,
Checked = item.Checked });
}
estudanteViewModel.Cursos = checkboxListCursos;
return View(estudanteViewModel);
}
|
Observe que neste código estamos usando e retornando a nossa ViewModel EstudantesViewModel para poder selecionar os cursos e exibí-los na página do estudante onde o estudante poderá selecionar um curso marcando o checkbox.
Devemos também alterar o método Edit/Post para poder salvar a seleção feita pelo usuário.
// POST: Estudantes/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EstudantesViewModel estudante )
{
if (ModelState.IsValid)
{
var estudanteSelecionado = db.Estudantes.Find(estudante.EstudanteId);
estudanteSelecionado.Nome = estudante.Nome;
estudanteSelecionado.Idade = estudante.Idade;
estudanteSelecionado.Sexo = estudante.Sexo;
foreach(var item in db.CursosEstudantes)
{
if (item.EstudanteId==estudante.EstudanteId)
{
db.Entry(item).State = EntityState.Deleted;
}
}
foreach(var item in estudante.Cursos)
{
if(item.Checked)
{
db.CursosEstudantes.Add(new CursoEstudante() { EstudanteId = estudante.EstudanteId,
CursoId = item.Id });
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(estudante);
}
|
Neste código estamos usando a viewmodel EstudantesViewModel e incluindo na tabela CursosEstudantes as seleções de cursos feitas pelo estudante.
Agora basta alterarmos as views geradas para o estudante na pasta Views/Estudantes.
Ajustando as Views para o Estudante : Edit.cshtml e Details.cshtml
Abra o arquivo Edit.cshtml gerado pelo Scaffolding na pasta Views/Estudantes e altere o seu código conforme mostrado abaixo no código em azul:
@model Mvc_CodeFirst_MuitosMuitos.Models.EstudantesViewModel
@{
ViewBag.Title = "Edit";
}
<h2>Editar Estudante/Curso</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.EstudanteId)
<div class="form-group">
@Html.LabelFor(model => model.Nome, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Nome, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Nome, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Idade, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Idade, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Idade, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Sexo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Sexo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Sexo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Cursos, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@for (int i = 0; i < Model.Cursos.Count(); i++)
{
@Html.EditorFor(m => Model.Cursos[i].Checked)<text> </text>
@Html.DisplayFor(m => Model.Cursos[i].Nome)<text> </text>
<br />
@Html.HiddenFor(m => Model.Cursos[i].Nome)
@Html.HiddenFor(m => Model.Cursos[i].Id)
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Salvar" class="btn btn-success" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Retornar", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
|
Note que alteremos Model usado na página para
EstudantesViewModel.
Abra o arquivo Details.cshtml na pasta Views/Estudantes e altere o
código conforme mostrado abaixo em azul:
@model Mvc_CodeFirst_MuitosMuitos.Models.EstudantesViewModel
@{
ViewBag.Title = "Details";
}
<h2>Detalhes Estudante</h2>
<div>
<hr />
<dl class="dl-horizontal">
<dt>@Html.DisplayNameFor(model => model.Nome)</dt>
<dd> @Html.DisplayFor(model => model.Nome)</dd>
<dt>@Html.DisplayNameFor(model => model.Idade)</dt>
<dd> @Html.DisplayFor(model => model.Idade)</dd>
<dt>@Html.DisplayNameFor(model => model.Sexo)</dt>
<dd>@Html.DisplayFor(model => model.Sexo)</dd>
<dt>@Html.DisplayNameFor(model => model.Cursos)</dt>
<dd>
@if (Model.Cursos != null)
{
foreach (var item in Model.Cursos)
{
if (item.Checked)
{
<div><span class="glyphicon glyphicon-list-alt"></span><Text> </Text>
@item.Nome</div>
}
}
}
else
{
<span class="glyphicon glyphicon-list-alt"></span><text>Sem Cursos Registrados</Text>
}
</dd>
</dl>
</div>
<p>
@Html.ActionLink("Edit", "Editar", new { id = Model.EstudanteId }) |
@Html.ActionLink("Retornar", "Index")
</p>
|
Agora é só alegria...
Executando o projeto iremos obter o seguinte resultado:
Pegue o projeto aqui : Mvc_CodeFirst_MuitosMuitos.zip (sem as referências)
Mas longe esteja de mim gloriar-me, a
não ser na cruz de nosso Senhor Jesus Cristo, pela qual o mundo está crucificado
para mim e eu para o mundo.
Gálatas 6:14
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? |
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Curso Fundamentos da Programação Orientada a Objetos com VB .NET
NET - Novidades do Visual Studio 2012 - ASP .NET MVC 5 - Macoratti
ASP .NET MVC - Controlando Depósitos Bancários (MVC 5 ... - Macoratti
ASP .NET MVC 5 - Catálogo de Clientes com Fotos e Paginação ...
ASP .NET MVC - Criando uma aplicação básica - CRUD ... - Macoratti