Fala galera, beleza?

Vou demonstrar como modelar um pequeno contexto usando o Entity Framework 6.1 com mapeamento Fluent Api onde precisamos escrever um pouco mais de código, porém não sujamos nossas entidades de domínio com anotações.

Como se trata de um artigo com tutorial, achei melhor dividir em duas partes…

Mapeamento com Entity Framework Code First (Fluent Api) – Parte 1
Mapeamento com Entity Framework Code First (Fluent Api) – Parte 2

 

Qual a diferença entre Data Annotations e Fluent Api?

Quando usado o mapeamento por Data Annotations, as propriedades das entidades POCO (Plain Old CLR Object) são decoradas com atributos, fazendo necessário referência externa, além da “sujeira” dos metadados.


public int ClienteId {get; set;}
[MaxLength(150), Required]
public string Nome { get; set; }

No Fluent Api usamos métodos encadeados fora das entidades, as mesmas permanecem “limpas”, porém precisamos escrever mais código.

HasKey(x => x.ClienteId);
Property(x => x.Nome)
     .HasMaxLength(150)
     .HasRequired();

 

Por que usar Fluent Api?
Falando de sistemas construídos usando DDD (Domain Driven Design), logo vem a primeira preocupação:

Como isolar a camada de domínio de tudo, principalmente da persistência de dados?

Como visto anteriormente, usando Data Annotations, somos obrigados a referenciar a dll System.ComponentModel.DataAnnotations na camada de domínio, pois temos que decorar as propriedades com as anotações. A única forma de usufruir do Code First e manter nossas entidades “limpas” sem referências externas, é mapeando o contexto na abordagem Fluent Api. Com esse modelo, as entidades de domínio se tornam “ignorantes” para as configurações de banco de dados, já que esses detalhes ficam isolados na camada de persistência, que por sua vez, sabe quais as entidades que está configurando. Veja a Figura 01.
Mapeamento-com-Entity-Framework-Code-First-FluentApi-01

Figura 01 – Abordagem usando Fluent Api.

 

Tudo que é possível configurar com Data Annotations é possível configurar com o Fluent Api, já o inverso não é possível tornando o aprendizado do Fluent Api OBRIGATÓRIO em sistemas mais complexos, o modo com Data Annotations seria mais apropriado em contextos mais simples permitindo maior acoplamento.

Neste artigo vamos construir um pequeno contexto de uma escola e demonstrar as configurações de banco de dados, mapeamento e relacionamentos entre as entidades, que serão espelhadas como relacionamento de tabela no banco de dados.

Bom, abra o Visual Studio 2013 New Project > Other Project Type > Visual Studio Solution, nomeie como EntityFrameworkEscola, após criar a solução, adicione 3 projetos Visual C# conforme a Figura 2.

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

Figura 02 – Descrição dos Projetos.

 

Nos projetos EntityFrameworkEscola.ConsoleEntityFrameworkEscola.DataAccess, instale o Entity Framework via Nuget.

PM> Install-Package EntityFramework

Adicione a referência do projeto  EntityFrameworkEscola.Domain nos projetos EntityFrameworkEscola.DataAccess e EntityFrameworkEscola.Console e a referência do projeto EntityFrameworkEscola.DataAccess no projeto EntityFrameworkEscola.Console.

A arquitetura deverá ficar como demonstrado na Figura 03.

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

Figura 03 – Arquitura de Teste.

Obs: O projeto EntityFrameworkEscola.Console simula uma camada de apresentação e claro, que em um projeto real, o mesmo não iria referenciar a camada de persistência, teríamos que usar algum container de DI, interfaces e etc, porém para simplificar este artigo, vamos trabalhar dessa forma.

No projeto EntityFrameworkEscola.Domain, adicione uma pasta chamada Entities e dentro da pasta, crie as seguintes classes conforme as listagens abaixo:

Aluno

namespace EntityFrameworkEscola.Domain.Entities
{
        public class Aluno: Pessoa
        {
             public Aluno()
             {
                Usuario = new Usuario();
             }
             public int AlunoId { get; set; }
             public virtual Turma Turma { get; set; }
             public virtual Usuario Usuario { get; set; }
        }
}

Listagem 01 – Aluno.cs

 

Curso

using System.Collections.Generic;
namespace EntityFrameworkEscola.Domain.Entities
{
       public class Curso
       {
             public Curso()
             {
                  ProfessorLista = new List<Professor>();
                  Ativo = true;
             }
             public int CursoId { get; set; }
             public string Numero { get; set; }
             public string Descricao { get; set; }
             public bool Ativo { get; set; }
             public virtual ICollection<Professor> ProfessorLista { get; set; }
             public override string ToString()
             {
                 return Descricao;
             }
       }
}

Listagem 02 – Curso.cs

Pessoa

namespace EntityFrameworkEscola.Domain.Entities
{
      public abstract class Pessoa
      {
          public string Nome { get; set; }
      }
}

Listagem 03 – Pessoa.cs

 

Professor

using System.Collections.Generic;
namespace EntityFrameworkEscola.Domain.Entities
{
      public class Professor: Pessoa
      {
           public Professor()
           {
              CursoLista = new List<Curso>();
              TurmaLista = new List<Turma>();
           }
           public int ProfessorId { get; set; }
           public virtual ICollection<Curso> CursoLista { get; set; }
           public virtual ICollection<Turma> TurmaLista { get; set; }
           public override string ToString()
           {
               return Nome;
           }
      }
}

Listagem 04 – Professor.cs

 

Turma

using System;
using System.Collections.Generic;
namespace EntityFrameworkEscola.Domain.Entities
{
      public class Turma
      {
          public Turma()
          {
               AlunoLista = new List<Aluno>();
          }
          public int TurmaId { get; set; }
          public DateTime DataInicio { get; set; }
          public DateTime DataTermino { get; set; }
          public virtual Professor Professor { get; set; }
          public virtual Curso Curso { get; set; }
          public virtual ICollection<Aluno> AlunoLista { get; set; }
          public override string ToString()
          {
                return String.Format("Turma: {0} - Data Início: {1} - Professor: {2}", Curso.Descricao, DataInicio.ToShortDateString(), Professor.Nome);
          }
      }
}

Listagem 05 – Turma.cs

Usuario

namespace EntityFrameworkEscola.Domain.Entities
{
     public class Usuario
     {
          public int UsuarioId { get; set; }
          public string Email { get; set; }
          public string Senha { get; set; }
     }
}

Listagem 06 – Usuario.cs

 

Bom, temos todas as nossas entidades criadas, agora vamos para o projeto EntityFrameworkEscola.DataAccess, crie uma classe com o nome DataContext, nela vamos realizar algumas configurações válidas para o nosso contexto, veja confrome a Listagem 07

using EntityFrameworkEscola.Domain.Entities;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace EntityFrameworkEscola.DataAccess
{
       public class DataContext : DbContext
       {
             public DataContext()
                     : base("DataContext")
             {
                  Configuration.LazyLoadingEnabled = false;
                  Configuration.ProxyCreationEnabled = false;
             }
             public DbSet<Aluno> Alunos { get; set; }
             public DbSet<Turma> Turmas { get; set; }
             public DbSet<Curso> Cursos { get; set; }
             public DbSet<Professor> Professores { get; set; }
             public DbSet<Usuario> Usuarios { get; set; }
             protected override void OnModelCreating(DbModelBuilder modelBuilder)
             {
                  modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
                  modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
                  modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
                  modelBuilder.Properties()
                         .Where(p => p.Name == p.ReflectedType.Name + "Id")
                         .Configure(p => p.IsKey());
                 modelBuilder.Properties<string>()
                        .Configure(p => p.HasColumnType("varchar"));
                  modelBuilder.Properties<string>()
                        .Configure(p => p.HasMaxLength(100));
             }
       }
}

Listagem 07 – DataContext.cs

Vamos entender o que significa cada uma dessas configurações:

A classe DataContext deve herdar o DbContext do Entity Framework, no construtor da classe base, o nome da connection string deve ser passado por parâmetro, se nada for informado, ele vai procurar uma connection string com o mesmo nome da classe que está configurando os dados, no caso do nosso exemplo, a classe DataContext.

   public DataContext()
        : base("DataContext")//Passado por parâmetro o nome da connection string
   {
        ...
   }

Listagem 08 – Contrutor do DataContext.cs

Connection String que está no projeto EntityFrameworkEscola.Console:

<connectionStrings>
 <add name="DataContext"
      connectionString="Data Source=(localdb)\v11.0;
      Initial Catalog=EFEscola;
      Integrated Security=True;
      Pooling=False"
      providerName="System.Data.SqlClient"/>
 </connectionStrings>

Listagem 09 – Connection String.

 

Dentro do construtor, eu costumo desabilitar o Lazy Loading, esse mecanismo faz com que o Entity Framework carregue automaticamente os relacionamentos em memória, o que pode causar perda de performance ao fazer um select na base de dados, quando essa opção é desabilitada (deixado como false), ele não carrega as dependências automaticamente e quando for necessário carregar, basta usar o método Include():

   Configuration.LazyLoadingEnabled = false;

Listagem 10 – Desabilitando o Lazy Loading

 

Eu também costumo desabilitar a criação de proxy, o Entity Framework por padrão cria um proxy toda vez que é instanciado uma entidade POCO para que possa ser realizado eventuais mudanças e fazer o carregamento automático das propriedades virtuais, como não estamos usando o Lazy Loading habilitado, não tem muito sentido manter a criação de proxy habilitada também.

     Configuration.ProxyCreationEnabled = false;

Listagem 11 – Desabilitando a criação de proxy

 

Veja na Figura 04 o proxy em ação quando instanciamos uma entidade POCO

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

Figura 04 – Proxy habilitado

 

 

Na Listagem 12, temos as propriedades DbSet que vão realizar o mapeamento das tabelas do banco de dados nas entidades POCO

public DbSet<Aluno> Alunos { get; set; }
public DbSet<Turma> Turmas { get; set; }
public DbSet<Curso> Cursos { get; set; }
public DbSet<Professor> Professores { get; set; }
public DbSet<Usuario> Usuarios { get; set; }

Listagem 12 – Mapeamento das entidades POCO

 

Na classe DataContext, como visto anteriormente, fizemos um override no método OnModelCreating, nele vamos realizar algumas configurações que serão válidas para todo o contexto, como definição de nomenclatura das tabelas, definição de chave primária, entre outras.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    //Aqui vamos remover a pluralização padrão do Etity Framework que é em inglês
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    /*Desabilitamos o delete em cascata em relacionamentos 1:N evitando
     ter registros filhos     sem registros pai*/
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

    //Basicamente a mesma configuração, porém em relacionamenos N:N
    modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

   /*Toda propriedade do tipo string na entidade POCO
    seja configurado como VARCHAR no SQL Server*/
    modelBuilder.Properties<string>()
              .Configure(p => p.HasColumnType("varchar"));

     /*Toda propriedade do tipo string na entidade POCO seja configurado como VARCHAR (150) no banco de dados */
     modelBuilder.Properties<string>()
            .Configure(p => p.HasMaxLength(150));

  /*Definimos usando reflexão que toda propriedade que contenha
 "Nome da classe" + Id como "CursoId" por exemplo, seja dada como
 chave primária, caso não tenha sido especificado*/
    modelBuilder.Properties()
       .Where(p => p.Name == p.ReflectedType.Name + "Id")
       .Configure(p => p.IsKey());
}

Listagem 13 – Método OnModelCreating

 

Bom galera, terminamos nosso contexto com o Entity Framework, na segunda parte desse artigo, vamos construir os relacionamentos entre as entidades POCO que por sua vez, serão replicados como relacionamentos de tabela (MER) no SQL Server, vamos trabalhar com as estruturas 1:N, N:N e 1:1. Todo esse trabalho na configuração vai finalmente fazer sentido.

Até lá!

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