Depois de toda a configuração do contexto no Entity Framework visto na primeira parte do artigo, vamos focar nas entidades POCO e construir os relacionamentos entre elas com a abordagem Fluent Api.

Antes de montar as relações de tabela, no projeto EntityFramewokEscola.DataAccess adicione uma pasta chamada Map, nessa pasta vamos criar as classes de mapeamento.

Vamos iniciar já com um relacionamento N:N: Curso – Professor
Regra:
Um Curso pode ser ministrado por um ou muitos professores e um professor pode ministrar um ou muitos cursos:

Veja a MER (Modelo Entidade Relacionamento)

Mapeamento-com-Entity-Framework-Code-First-FluentApi-06

Dentro da pasta Map, crie uma classe chamada CursoMap, a mesma deverá ficar como na imagem abaixo:

using EntityFrameworkEscola.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace EntityFrameworkEscola.DataAccess.Map
{
        public class CursoMap : EntityTypeConfiguration<Curso>
        {
              public CursoMap()
              {
                  /*O método ToTable define qual o nome que será
                  dado a tabela no banco de dados*/
                  ToTable("Curso");

                  //Definimos a propriedade CursoId como chave primária
                  HasKey(x => x.CursoId);

                  //Descricao terá no máximo 150 caracteres e será um campo "NOT NULL"
                  Property(x => x.Descricao).HasMaxLength(150).IsRequired();

                  HasMany(x => x.ProfessorLista)
                            .WithMany(x => x.CursoLista)
                            .Map(m =>
                            {
                               m.MapLeftKey("CursoId");
                               m.MapRightKey("ProfessorId");
                               m.ToTable("CursoProfessor");
                            });
                }
          }
}

Vamos entender o que significa todos esses métodos. Como falamos na primeira parte, a entidade POCO lá no projeto Domain continua “limpa” sem a necessidade de nenhuma referência externa, já no projeto DataAccess, estamos fazendo todo o mapeamento das classes que no SQL Server serão espelhadas como tabelas.

Queria chamar a atenção em um detalhe importante, como vimos na MER, quando temos um relacionamento N:N, sempre teremos que ter uma terceira tabela para relacionar os ID’s dos participantes, como estamos usando um ORM, que tem como premissa trazer o contexto de banco de dados para a POO (Programação Orientada a Objeto), não precisamos criar uma terceira classe chamada CursoProfessor para estar em conformidade com a MER, basta definir a terceira tabela através de métodos encadeados, veja na imagem abaixo:

        /*Curso tem uma lista de Professores*/
         HasMany(x => x.ProfessorLista)
           .WithMany(x => x.CursoLista) //...e professores uma lista de Cursos
           .Map(m => //esse relacionamento será mapeado em uma terceira tabela
              {
                 m.MapLeftKey("CursoId"); //chave da esquerda será de CursoId
                 m.MapRightKey("ProfessorId"); //Chave da direita será ProfessorId
                 m.ToTable("CursoProfessor");// e o nome da tabela será CursoProfessor
              });

Obs: Na verdade, mesmo sem nenhum mapeamento, o Entity Framework tem a inteligência de saber quando duas entidades POCO possuem listas entre si, ele automaticamente cria a terceira tabela, porém vai criar com a nomenclatura padrão. Usando o mapeamento imperativo no C#, definimos o nosso padrão de nomenclatura de campos e tabela.

Na pasta Map, crie uma classe chamada ProfessorMap e veja como a mesma deve estar:

using EntityFrameworkEscola.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace EntityFrameworkEscola.DataAccess.Map
{
      public class ProfessorMap : EntityTypeConfiguration<Professor>
      {
          public ProfessorMap()
          {
              ToTable("Professor");
              HasKey(x => x.ProfessorId);
              Property(x => x.Nome).HasMaxLength(150).IsRequired();
          }
      }
}

Agora vamos configurar o relacionamento de Curso, Professor e Turma.
Regra: Uma Turma deve ter obrigatoriamente um Curso e um Professor que ministre aquele curso específico, assim como um professor e um curso pode estar em N Turmas, teremos um relacionamento 1:N. Veja na MER como ficará:
Mapeamento-com-Entity-Framework-Code-First-FluentApi-07

Legal, crie uma classe dentro da pasta Map chamada TurmaMap e veja suas configurações:

using EntityFrameworkEscola.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace EntityFrameworkEscola.DataAccess.Map
{
     public class TurmaMap : EntityTypeConfiguration<Turma>
     {
         public TurmaMap()
         {
            ToTable("Turma");
            HasKey(x => x.TurmaId);

           /*Na entidade Turma, a propriedade do tipo Curso é obrigatória*/
            HasRequired(x => x.Curso)
               .WithMany() //Curso pode ter muitas turmas
               .Map(m => m.MapKey("CursoId"));//a chave estrangeira em Turma é CursoId

            /*Novamente, em Turma a propriedade do tipo Professor é requerida*/
            HasRequired(x => x.Professor)
               .WithMany(x => x.TurmaLista) //a classe Professor pode ter uma lista de Turma
               .Map(m => m.MapKey("ProfessorId"));//a chave estrangeira é ProfessorId
         }
     }
}

Continuando, crie uma classe chamada AlunoMap dentro da pasta Map. 
Regra: Obrigatoriamente, um Aluno deve ter uma Turma e uma Turma pode conter um ou muitos Alunos.

MER do relacionamento entre Turma e Aluno

Mapeamento-com-Entity-Framework-Code-First-FluentApi-08

Veja como a classe AlunoMap deve ficar:

using EntityFrameworkEscola.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace EntityFrameworkEscola.DataAccess.Map
{
     public class AlunoMap : EntityTypeConfiguration<Aluno>
     {
         public AlunoMap()
         {
            ToTable("Aluno");
            HasKey(x => x.AlunoId);

            //1:N - 1 aluno DEVE ter 1 turma e 1 turma pode ter muitos alunos
            HasRequired(x => x.Turma)
              .WithMany(x => x.AlunoLista)
              .Map(m => m.MapKey("TurmaId"));

            //1:1 - 1 aluno deve ter apenas 1 usuário
            HasRequired(x => x.Usuario)
              .WithRequiredPrincipal();
         }
     }
}

Finalmente, vamos ver como fica um relacionamento 1:1…

Regra: Quando um Aluno for criado, deve existir um Usuário e vice versa. Vejamos como fica a MER:

Mapeamento-com-Entity-Framework-Code-First-FluentApi-09

Crie uma classe chamada UsuarioMap dentro da pasta Map, a mesma deve ficar como a imagem abaixo:

using EntityFrameworkEscola.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration;

namespace EntityFrameworkEscola.DataAccess.Map
{
     public class UsuarioMap : EntityTypeConfiguration<Usuario>
     {
         public UsuarioMap()
         {
            ToTable("Usuario");
            HasKey(x => x.UsuarioId);
            Property(x => x.Email).IsRequired().HasMaxLength(150);
            Property(u => u.Senha).HasMaxLength(20).IsRequired();
          }
      }
}

Finalizando…

Vá até o Package Manager Console e adicione o Migrations no projeto EntityFrameworkEscola.DataAccess com o seguinte comando:

PM> Enable-Migrations

No projeto  EntityFrameworkEscola.DataAccess, será criado automaticamente uma pasta chamada Migrations, dentro da pasta terá uma classe chamada Configuration, habilite o versionamento automático da base de dados.

   public Configuration()
   {
        AutomaticMigrationsEnabled = true;
   }

No método Seed, vamos inserir alguns dados iniciais…

 protected override void Seed(EntityFrameworkEscola.DataAccess.DataContext context)
 {
     Professor professor1 = new Professor();
     professor1.Nome = "Professor Um";

     Professor professor2 = new Professor();
     professor2.Nome = "Professor Dois";

     Curso curso1 = new Curso();
     curso1.Numero = "70-480";
     curso1.Descricao = "Programming in HTML5 with JavaScript and CSS3";
     curso1.ProfessorLista.Add(professor1);
     curso1.ProfessorLista.Add(professor2);

     Curso curso2 = new Curso();
     curso2.Numero = "70-486";
     curso2.Descricao = "Developing ASP.NET MVC 4 Web Applications";
     curso2.ProfessorLista.Add(professor2);

     context.Cursos.Add(curso1);
     context.Cursos.Add(curso2);

 }

Volte a classe DataContext, no método OnModelCreating, vamos inserir o mapeamento das nossas entidades POCO, inclua dentro do método conforme a listagem abaixo:

      ...

 modelBuilder.Configurations.Add(new AlunoMap());
 modelBuilder.Configurations.Add(new TurmaMap());
 modelBuilder.Configurations.Add(new CursoMap());
 modelBuilder.Configurations.Add(new ProfessorMap());
 modelBuilder.Configurations.Add(new UsuarioMap());

No projeto EntityFrameworkEscola.Console, na classe Program, veja como fica para testarmos tudo o que fizemos, coloque um break point e brinque um pouco para melhor compreensão:

using EntityFrameworkEscola.DataAccess;
using EntityFrameworkEscola.Domain.Entities;
using System;
using System.Linq;

namespace EntityFrameworkEscola
{
      class Program
      {
            static void Main(string[] args)
            {
                DataContext db = new DataContext();

                var cursos = db.Cursos
                        .Include("ProfessorLista")
                        .ToList();

                var curso = cursos.FirstOrDefault(x => x.Numero == "70-486");
                var professor = curso.ProfessorLista.FirstOrDefault(x => x.ProfessorId == 2);

                Turma turma = new Turma();
                turma.DataInicio = DateTime.Now;
                turma.DataTermino = DateTime.Now.AddDays(10);
                turma.Curso = curso;
                turma.Professor = professor;

                Aluno aluno = new Aluno();
                aluno.Nome = "Diego Neves";

                Usuario usuario = new Usuario();
                usuario.Email = "[email protected]";
                usuario.Senha = "123456";

                aluno.Usuario = usuario;

                Aluno aluno2 = new Aluno();
                aluno2.Nome = "Raquel Mota";

                Usuario usuario2 = new Usuario();
                usuario2.Email = "[email protected]";
                usuario2.Senha = "123456";

                aluno2.Usuario = usuario2;

                turma.AlunoLista.Add(aluno);
                turma.AlunoLista.Add(aluno2);

                db.Turmas.Add(turma);
                db.SaveChanges();

                var turmas = db.Turmas
                     .Include("Professor")
                     .Include("Curso")
                     .Include("AlunoLista")
                     .ToList();

                var usuarios = db.Usuarios
                             .ToList();

                foreach (var turmaItem in turmas)
                {
                     Console.WriteLine("Turma: "+ turmaItem.Curso.Descricao);
                     Console.WriteLine("Início: " + turmaItem.DataInicio.ToShortDateString());
                     Console.WriteLine("Previsto: " + turmaItem.DataTermino.ToShortDateString());
                     Console.WriteLine("Professor: "+ turmaItem.Professor.Nome);
                     Console.WriteLine("------------------------ Alunos Matriculados ------------------------");
                foreach (var alunoItem in turmaItem.AlunoLista)
                {
                    Console.WriteLine(String.Format("Nome: {0} - E-mail: {1}", alunoItem.Nome, usuarios.FirstOrDefault(x => x.UsuarioId == alunoItem.AlunoId).Email));
 }
                    Console.WriteLine(Environment.NewLine);
                }

                Console.ReadKey();

          }
      }
}

Dúvidas, sugestões e blá, blá, blá, [email protected]

Projeto no GitHub

Valeu pela companhia até agora e até a próxima!!!

Referências:

Configurando Relacionamentos com Fluent Api
https://msdn.microsoft.com/en-us/data/jj591620.aspx

Relacionamento 1:N com Fluent Api
http://jonatassaraiva.net/post/12/01/2013/Entity-Framework-5-Fluent-API-Relacionamento-1-para-N

Convenções com Code First
https://msdn.microsoft.com/en-us/data/jj819164.aspx

Code First e Migrations
https://msdn.microsoft.com/en-us/data/jj591621.aspx

Diego Neves

Desenvolvedor de software, graduado em Análise e Desenvolvimento de Sistemas pela Universidade Nove de Julho, atualmente trabalha na FCamara e tem foco no mundo .NET, possui algumas certificações como MCSD Web Applications, MCSD App Builder, MCSA Web Applications, Microsoft Specialist, MCP e ITIL V3 Foundation.

Facebook LinkedIn 

Comentários

comentarios