You are on page 1of 32

O que uma ferramenta de mapeamento

objeto-relacional?
Todos ns, desenvolvedores de software, sabemos da importncia de banco de dados
em nossos sistemas. Necessitamos, a todo instante, realizar consultas aos dados para
atender as solicitaes do usurio, bem como, registrar qualquer informao passada por
este.
Atualmente possumos muitos bancos de dados relacionais, dentre os mais conhecidas
podemos citar: Oracle, MS SQL Server, MySql, etc. Nessas ferramentas, as
informaes so armazenadas como registros de tabelas.
Por exemplo, imagine o sistema para gerenciamento de produtos em uma loja virtual. O
banco de dados para armazenar os produtos disponveis para venda teria uma tabela
chamada Produtos, composta por campos que caracterizam um produto, tais como:
Cdigo, Nome, Preo, Categoria, etc.
Sem a utilizao de alguma ferramenta, o trabalho do programador seria rduo e muito
vulnervel a falhas. Imagine o mapeamento de uma tabela Usurios criada atravs do
seguinte comando SQL:
create table Users (
id int UNSIGNED NOT NULL AUTO_INCREMENT,
email varchar (255) NOT NULL,
password varchar (20) NOT NULL,
name varchar (255) NOT NULL,
active bit (1),
date datetime NOT NULL,
PRIMARY KEY(id)
)

Em nosso sistema temos a necessidade de listar todos os usurios cadastrados no banco.


Quando queremos acessar o banco de dados, precisamos de uma conexo:
SqlConnection conexao = new SqlConnection("String de conexo com o MS SQL");
conexao.Open();

Depois de abrir a conexo, precisamos preparar um comando para o banco de dados que
enviar umSELECT que devolver a lista de usurios:
SqlCommand comando = new SqlCommand(
"SELECT id, email, password, name, active, date FROM Users",
conexao);
comando.CommandType = System.Data.CommandType.Text;
O comando preparado executado atravs do mtodo ExecuteReader,

esse mtodo

devolve um objeto especializado em ler o resultado da busca:


IDataReader reader = comando.ExecuteReader();

Agora que temos o leitor do resultado, precisamos transform-lo em uma lista de


modelos da aplicao. O IDataReader possui o mtodo Read que responsvel por ler os
resultados devolvidos pela busca.
Cada vez que chamamos o Read, estamos avanando para o prximo registro da busca.
Enquanto for possvel avanar, o Read devolve o valor true, o false devolvido
quando no h mais registros na busca. O cdigo para ler todos os registros fica da
seguinte forma:
while (reader.Read())
{

// l o registro
}

Podemos acessar as informaes do registro atual utilizando a notao de array, da


mesma forma que fazemos com um dicionrio do C#, porm os tipos dos dados
devolvidos pelo reader so tipos do banco de dados e, portanto, precisamos convertlos para os tipos do c#. Esse trabalho ser feito pela classe Convert:
while(reader.Read())
{
int id = Convert.ToInt32(reader["id"]);
}

Os dados que estamos lendo pertencem ao modelo Usuario, ento vamos guard-los em
uma instncia dessa classe
while(reader.Read())
{
Usuario usuario = new Usuario();
usuario.Id = Convert.ToInt32(reader["id"]);
usuario.Email = Convert.ToString(reader["email"]);
usuario.Senha = Convert.ToString(reader["pasword"]);
usuario.Nome = Convert.ToString(reader["name"]);
usuario.Ativo = Convert.ToBoolean(reader["active"]);
usuario.DataCadastro = Convert.ToDateTime(reader["date"]);
}

O loop l o registro, cria o usurio com os dados preenchidos e imediatamente perde a


referncia para o usurio criado. Para no perdemos a referncia, vamos guard-la em
uma lista:
IList<Usuario> usuarios = new List<Usuario>();
while(reader.Read())
{
Usuario usuario = new Usuario();
usuario.Id = Convert.ToInt32(reader["id"]);
usuario.Email = Convert.ToString(reader["email"]);
usuario.Senha = Convert.ToString(reader["pasword"]);
usuario.Nome = Convert.ToString(reader["name"]);
usuario.Ativo = Convert.ToBoolean(reader["active"]);
usuario.DataCadastro = Convert.ToDateTime(reader["date"]);
usuarios.Add(Usuario);
}

Agora que terminamos de fazer a query, precisamos fechar o reader e a conexo.


reader.Close();
conexao.Close();

O cdigo para acessar o banco de dados trabalhoso e repetitivo, alm disso, quando
fazemos queries que envolvem valores passados pelo usurio, podemos facilmente
inserir vulnerabilidades que permitem ataques como o SQL Injection (Tcnica utilizada
por hackers para enviar comandos nocivos base de dados, atravs de campos do
formulrio ou URLs, por exemplo).
Alm do cdigo repetitivo e dos problemas de segurana, o que acontece quando
precisamos trocar o banco MySQL, utilizado atualmente, para o Oracle? Nesse caso,
teremos que modificar o cdigo do sistema inteiro, alm das SQLs, para poder suportar
o novo banco.
Esses so apenas alguns dos problemas que temos quando lidamos com o banco de
dados diretamente, mas reparem que para construirmos uma query na tabela de usurios,
precisamos apenas olhar a estrutura da classe Usuario, ou seja, podemos ter uma
ferramenta que dada uma classe, consegue construir as queries necessrias. Ferramentas

que fazem o mapeamento do mundo orientado a objetos (classes e objetos) para o


mundo relacional so chamadas de mapeadores objeto relacional, ou ORM (Object
Relational Mapper). Nesse mapeamento, classes se transformam em tabelas e objetos
em registros das tabelas.
Nesse curso, aprenderemos como funciona o ORM da Microsoft. O Entity Framework!

Instalao do Entity Framework


O Entity Framework uma ferramenta de mapeamento objeto relacional desenvolvido
pela Microsoft, nele as entidades do banco de dados so mapeadas para colees de
dados que podem ser utilizadas no LINQ.
Para exemplificarmos o uso do Entity Framework, criaremos uma loja virtual
simplificada, em um projeto chamado LojaEF. Nesse projeto, criaremos classes que
possuem representao no banco de dados, chamaremos essas classes de Entidades.
Vamos criar um projeto do tipo Console Application, que chamaremos de LojaEF
Para utilizarmos o utilizarmos o entity framework no projeto, precisamos instal-lo e
para isso, utilizaremos o gerenciador de pacotes padro do Visual Studio, o NuGet.
Dentro do Visual Studio, clique com o boto direito no projeto Loja e escolha a
opo Manage Nuget Packages. Uma janela como a da imagem abaixo ser aberta.

Na aba Online Packages, busque por Entity Framework e aps encontrar clique no
boto Install.

O prprio Nuget se encarrega de efetuar o download do framework e j referenciar as


dlls necessrias ao seu projeto. Pronto! Voc j pode utilizar o Entity Framework em
seu projeto.

Mapeando a primeira entidade com o Entity


Framework
Agora que temos o Entity Framework instalado no projeto, podemos escrever a primeira
entidade, a classe Usuario. Ela ser uma entidade do domnio da aplicao, que ficar
dentro do namespaceLojaEF.Entidades
O usurio ter um ID, que ser o identificador nico do usurio e um Nome:
public class Usuario
{
public int ID { get; set; }
public string Nome { get; set; }
}

Quando utilizamos o Entity Framework, no precisamos mais nos preocupar com o


banco de dados. Essa responsabilidade agora do ORM, nossa preocupao ser apenas
com o domnio da aplicao.

Agora que a classe foi criada, precisamos dizer ao Entity Framework que ela representa
uma entidade do banco de dados. Para isso, ela deve ser colocada dentro de uma classe
que herda de DbContext, o contexto do Entity Framework:
public class EntidadesContext : DbContext
{
}

Essa classe funcionar como uma conexo com o banco de dados. Sempre que
quisermos gravar, recuperar, atualizar ou remover uma entidade faremos isso atravs do
contexto. Para mapearmos oUsuario, precisamos apenas definir uma propriedade do
tipo DbSet dentro do EntidadesContext:
public class EntidadesContext : DbContext
{
public DbSet<Usuario> Usuarios { get; set; }
}

O Entity Framework segue o padro Convention Over Configuration no mapeamento


das classes, o nome da tabela que conter as informaes de uma entidade o plural do
nome da classe. Cada atributo da entidade se transformar em uma coluna com o
mesmo nome do atributo. O ID da entidade ser o atributo que contm a palavra ID no
nome.
Agora que terminamos o mapeamento da classe, vamos utilizar o Entity Framework
para gerar as tabelas do banco de dados.
Nessa aplicao utilizaremos o SQLServer de testes que integrado ao visual studio.
Para criar o banco de dados que ser utilizado, dentro do Solution Explorer, clique com
o boto direito no projeto e selecione a opo Add > New Item. No canto esquerdo da
janela do New Item, escolha a opo Datae depois Service Based Database.
Escolha LojaEF.mdf como nome do novo banco de dados e depois clique no boto Add.

Quando clicarmos no Add, o Visual Studio abrir um assistente para nos ajudar a
configurar o banco de dados, o Data Source Configuration Wizard. Na janela aberta,
escolha a opo Dataset e depois clique em next.

Depois que o assistente terminar de configurar o banco de dados, clique no


boto Finish

Quando criamos o banco pelo Visual Studio, as configuraoes de acesso para o banco
criado so colocadas dentro do arquivo de configurao da aplicao, o App.config.
No App.config, as configuraes do banco de dados ficam dentro de uma tag chamada
connectionStrings:
<connectionStrings>
<add name="LojaEF.Properties.Settings.LojaEFConnectionString"
connectionString="Data
Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\LojaEF.mdf;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>

Para utilizarmos o Entity Framework com o banco criado, precisamos mudar o nome da
string de conexo para o nome do contexto que criamos para a aplicao, o
EntidadesContext:
<connectionStrings>
<add name="EntidadesContext"
connectionString="Data
Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\LojaEF.mdf;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>

Se deixarmos essa configurao padro na string de conexo, toda vez que executarmos
a aplicao, o banco de dados comear vazio! Para fazermos com que o banco no seja

apagado, colocaremos o caminho absoluto do banco na


configurao AttachDbFilename da string de conexo.
Quando queremos trabalhar com outros bancos de dados, precisamos apenas modificar
o arquivo de configurao da aplicao!
<connectionStrings>
<add name="EntidadesContext"
connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=Caminho Absolu to
Banco;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>

Se quisermos mudar o banco de dados da aplicao, precisamos apenas modificar as


configuraes que esto no App.config, no precisamos modificar o cdigo fonte da
aplicao!
Agora que j configuramos o banco de dados, vamos utilizar o contexto para gerar as
tabelas do banco. Para isso, utilizaremos a propriedade Database do contexto. Dentro do
mtodo Main, vamos criar um novo contexto:
class Program
{
static void Main(string [] args)
{
var contexto = new ExtidadesContext();
}
}

E agora que temos o contexto, precisamos apenas chamar a


mtodo CreateIfNotExists da propriedade Database do contexto.
var contexto = new EntidadesContext();
contexto.Database.CreateIfNotExists();

Com isso, o Entity Framework enviar os comandos para criar as tabelas no banco de
dados!
Alm disso, o contexto do Entity Framework pode ser utilizado para gravar usurios no
banco de dados.
Ento vamos criar um novo usurio chamado Victor:
Usuario victor = new Usuario { Nome = "Victor" };

E agora vamos gravar esse usurio no banco de dados. Para isso, utilizaremos a
propriedadeUsuarios que foi declarada no contexto. Essa propriedade representa um
conjunto de entidades que esto gravadas no banco e para inserir um usurio no banco,
precisamos apenas chamar o mtodoAdd nesse conjunto.
var contexto = new EntidadesContext();
Usuario victor = new Usuario { Nome = "Victor" };
contexto.Usuarios.Add(victor);

E agora que terminamos de adicionar as informaes no contexto, precisamos apenas


salvar as modificaes:
contexto.SaveChanges();

E por fim, precisamos fechar o contexto utilizando o mtodo Dispose.


contexto.Dispose();

O programa completo que gera as tabelas e grava a entidade fica da seguinte forma:

class Program
{
static void Main(string[] args)
{
var contexto = new EntidadesContext();
contexto.Database.CreateIfNotExists();
Usuario victor = new Usuario { Nome = "victor" };
contexto.Usuarios.Add(victor);
contexto.SaveChanges();
contexto.Dispose();
}
}

E com esse cdigo simples, conseguimos gravar o usurio no banco de dados utilizando
o Entity Framework.

Explicao
Agora que j entendemos o funcionamento bsico do Entity Framework, vamos
aprender como manipular os dados que temos no banco de dados.

Operaes bsicas utilizando o Entity


Framework
J vimos que o Entity Framework utiliza o LINQ para manipular os dados gravados no
banco de dados como se fossem colees do C#, com isso, podemos adicionar um novo
registro no banco fazendo simplesmente um Add na coleo:
var contexto = new EntidadesContext();
Usuario usuario = new Usuario()
{
Nome = "victor",
Senha = "123"
};
contexto.Usuarios.Add(usuario);

Agora para efetivarmos as modificaes, utilizamos o mtodo SaveChanges no contexto:


// envia as modificaes para o banco de dados.
contexto.SaveChanges();

Agora que j temos um objeto gravado, queremos recuper-lo. Para recuperarmos um


usurio pelo ID, utilizamos o mtodo Find do objeto DbSet.
var contexto = new EntidadesContext();
// busca o usurio de Id 1 do banco de dados.
Usuario usuario = contexto.Usuarios.Find(1L);
Console.WriteLine(usuario.Nome);

Se quisermos remover um usurio que foi buscado, precisamos apenas remov-lo da


coleo com o mtodo Remove
var contexto = new EntidadesContext();

Usuario usuario = contexto.Usuarios.Find(1L);


// remove o usurio da coleo.
contexto.Usuarios.Remove(usuario);
contexto.SaveChanges();

Isolando o acesso ao banco de dados com


DAOs
Nos exemplos que utilizamos at o momento, o cdigo de acesso ao banco est no
mtodo Main do programa. Com isso estamos dificultando a manuteno da aplicao.
Precisamos isolar o cdigo que acessa o banco de dados em classes especializadas em
fazer o acesso aos dados, que so conhecidas como Data Access Object ou DAO
Ento criaremos um DAO para isolar o cdigo que acessa as informaes do usurio no
banco de dados, esse cdigo ser isolado na classe UsuariosDAO
public class UsuariosDAO
{
public void Adiciona(Usuario usuario)
{
// cdigo para adicionar o usurio
}
public void Remove(Usuario usuario)
{
// cdigo para remover o usurio
}
public Usuario BuscaPorId(long id)
{
// busca o usurio por id
}
}

Cada um dos mtodos do DAO precisa do contexto para executar a query no banco de
dados, ento vamos abr-lo no construtor da classe:
public class UsuariosDAO
{
private EntidadesContext contexto;
public UsuariosDAO()
{
this.contexto = new EntidadesContext();
}
}

E agora podemos implementar os mtodos dos DAO:


public class UsuariosDAO
{
private EntidadesContext contexto;
public UsuariosDAO()
{
this.contexto = new EntidadesContext();
}
public void Adiciona(Usuario usuario)
{

this.contexto.Usuarios.Add(usuario);
this.contexto.SaveChanges();
}
public void Remove(Usuario usuario)
{
this.contexto.Usuarios.Remove(usuario);
this.contexto.SaveChanges();
}
public Usuario BuscaPorId(long id)
{
return this.contexto.Usuarios.Find(id);
}
}

E agora no cdigo do Main no precisamos mais nos preocupar com o cdigo de acesso
ao banco de dados, podemos simplesmente utilizar o DAO:
static void Main(string[] args)
{
Usuario usuario = new Usuario() { Nome = "victor", Senha = "123" };
UsuariosDAO dao = new UsuariosDAO();
dao.Adiciona(usuario);
}

Estado dos objetos


Agora que j aprendemos como fazer as operaes bsicas utilizando o entity
framework, vamos aprender como o entity framework gerencia o estado dos objetos do
contexto. Para ilustrar a explicao, vamos utilizar um banco de dados que contm um
usurio de id 1, chamado Victor e com senha 123.
Vimos que para buscar uma entidade do contexto, podemos utilizar o
mtodo Find do DbSet:
var contexto = new EntidadesContext();
Usuario usuario = contexto.Usuarios.Find(1);

Agora se quisermos mudar o nome do usurio para Victor Harada, precisamos


simplesmente mudar a propriedade nome do Usuario buscado:
usuario.Nome = "Victor Harada";

Agora que o usurio foi modificado, podemos salvar as modificaes no contexto


contexto.SaveChanges();

Quando executamos a linha acima, o Entity Framework sincroniza o estado dos objetos
com o banco de dados, ou seja, nesse ponto do cdigo o Entity Framework executa um
update no banco de dados para atualizar as informaes do usurio. Ento quando
buscamos um objeto, a entidade devolvida gerenciada.
Ao buscarmos um objeto, a entidade est em um estado chamado Unchanged, quando
executamos oSaveChanges todas as entidades Unchanged no so modificadas no banco.
Ao mudarmos o valor de uma propriedade o entity framework muda o estado da
entidade para Modified. Ao chamarmos oSaveChanges quando o contexto possui uma
entidade modificada, as modificaes so enviadas para o banco de dados.
Vimos que para gravarmos uma entidade, precisamos apenas adicion-la ao conjunto do
contexto:

var contexto = new EntidadesContext();


Usuario u = new Usuario { Nome = "rodrigo" };
contexto.Usuarios.Add(u);

Quando adicionamos uma nova entidade no contexto, ela fica em um estado chamado
Added.
Para removermos uma entidade, precisamos apenas chamar o mtodo Remove na coleo
do contexto passando a instncia que deve ser removida, quando fazemos isso, a
entidade entra em um estado chamado Deleted.
Toda entidade que no est associada ao contexto est em um estado
chamado Detached.

Cadastro de produtos para a loja


Para facilitar a busca de produtos, queremos divid-los em categorias. Vamos ento criar
a entidadesCategoria:
public class Categoria
{
public int ID { get; set; }
public string Nome { get; set; }
}

No c#, para representarmos que um produto possui uma categoria, precisamos apenas
criar uma nova propriedade do tipo Categoria na classe Produto:
public class Produto
{
// outras propriedades
public Categoria Categoria;
}

Quando criamos um atributo que representa um relacionamento dentro da classe,


precisamos marcar o atributo como virtual:
public class Produto
{
// outras propriedades
public virtual Categoria Categoria;
}

Para representarmos esse relacionamento no banco de dados, precisamos guardar o id da


categoria na tabela de produtos. Uma coluna que guarda o id para outra tabela
chamada de chave estrangeira e a propriedade que representa o relacionamento dentro
da classe chamada de Navigation Property. Alm de definirmos a Navigation Property
do produto para a categoria, tambm precisamos definir um atributo que ser a chave
estrangeira do produto para a categoria, por conveno, o nome do atributo que
representa a chave estrangeira ID, ou seja, no caso do produto teramos CategoriaID:
public class Produto
{
// propriedades do produto
// representa a chave estrangeira do produto para
// a categoria
public int CategoriaID { get; set; }
public Categoria Categoria { get; set; }
}

O tipo da chave estrangeira define a obrigatoriedade do relacionamento. Se quisermos


que todo produto tenha obrigatoriamente uma categoria, utilizamos o tipo int. Se a
categoria do produto for opcional, utilizamos o tipo int? como tipo da chave
estrangeira:
public class Produto
{
// propriedades do produto
// agora o produto tem opcionalmente uma categoria
public int? CategoriaID { get; set; }
public Categoria Categoria { get; set; }
}

Como a chave estrangeira est na tabela produtos, podemos ter vrios produtos
associados a mesma categoria, o que caracteriza um relacionamento muitos para um
(many to one).
Para que o entity framework reconhea essas classes como entidades, precisamos
adicion-las ao contexto:
public class EntidadesContexto
{
public DbSet<Usuario> Usuarios { get; set; }
public DbSet<Produto> Produtos { get; set; }
public DbSet<Categoria> Categorias { get; set; }
}

E com isso conseguimos mapear o produto com uma categoria, mas quando tentarmos
executar a aplicao, o entity framework jogar uma InvalidOperationException pois o
estado do banco de dados no est sincronizado com o estado dos modelos, ento
precisamos criar mais uma migration para incluir esses modelos no banco de dados.
Ento vamos abrir novamente o console do NuGet e executar o comando Add-Migration
CriaTabelasProdutoECategoria e depois vamos atualizar o banco com o
comando Update-Database.

Adicionando um produto com categoria


Quando queremos gravar um novo produto no banco de dados, precisamos,
inicialmente, criar e inicializar uma nova instncia do objeto Produto
Produto produto = new Produto();
produto.Nome = "Camiseta";
produto.Preco = 10.0;

E depois, para grav-lo no banco, devemos adicion-lo ao contexto e gravar as


modificaes:
var contexto = new EntidadesContext();
contexto.Produtos.Add(produto);
contexto.SaveChanges();

Temos duas formas de associar um produto com uma categoria. Na primeira,


precisamos apenas colocar uma instncia de Categoria no
atributo Categoria do Produto antes de grav-lo no banco de dados.
Produto produto = new Produto();
// inicializa o produto

Categoria categoria = new Categoria();


// inicializa a categoria
produto.Categoria = Categoria;
var contexto = new EntidadesContext();
contexto.Produtos.Add(produto);
contexto.SaveChanges();

Quando executamos esse cdigo, o Entity Framework tenta gravar o produto no banco
de dados, porm ele percebe que o produto est associado com uma categoria que ainda
no foi gravada e, portanto, tambm grava a categoria no banco de dados. Ao final desse
cdigo, teremos um produto associado com uma nova categoria.
Se quisermos associar o produto com uma categoria existente no banco, precisamos de
uma categoria que esteja associada com o contexto, ou seja, precisamos busc-la antes
de gravar o produto.
var contexto = new EntidadesContext();
var categoria = contexto.Categorias.Find(1L);
var produto = new Produto();
//inicializa o produto
produto.Categoria = categoria;
contexto.Produtos.Add(categoria);
contexto.SaveChanges();

Com isso, o Entity Framework percebe que o novo produto est associado a uma
categoria j existente e, portanto, adiciona apenas o produto ao banco de dados.
A segunda forma de associarmos o produto com uma categoria utilizando a
propriedade que representa a chave estrangeira do modelo. Se quisermos, por exemplo,
associar o novo produto com a categoria de id 1, que deve existir no banco de dados,
precisamos apenas colocar o id 1 na propriedade CategoriaID do novo produto:
produto.CategoriaID = 1;

Com isso, ao gravarmos o produto no banco de dados, o entity framework


automaticamente associar o novo produto com a categoria de id 1.

Categoria com lista de produtos


Agora que definimos o mapeamento do relacionamento many to one do produto com a
categoria, estamos interessados em pegar todos os produtos associados a uma
determinada categoria.
Quando olhamos o relacionamento do ponto de vista de um produto, temos muitos
produtos associados a uma instncia de categoria, porm do ponto de vista da categoria,
temos uma categoria associada a vrios produtos, ou seja, temos um relacionamento one
to many.
No mundo relacional, toda vez que temos um relacionamento many to one, existe,
automaticamente, um one to many, ou seja, no mundo relacional, os relacionamentos
so sempre bidirecionais. Na orientao a objetos, no temos relacionamentos
bidirecionais automticos, toda vez que queremos o relacionamento one to many,
devemos fazer o mapeamento explcito desse relacionamento.

Para mapear a lista de produtos na categoria, colocaremos, inicialmente, a lista como


uma propriedade da categoria:
public class Categoria
{
public int ID { get; set; }
public string Nome { get; set; }
public virtual IList<Produto> Produtos { get; set; }
}

Agora precisamos avisar o entity framework que o essa lista de produtos a outra ponta
do relacionamento many to one que colocamos dentro do produto. Fazemos isso atrves
do mtodoOnModelCreating do EntidadesContext:
public class EntidadesContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// mapeamento do relacionamento aqui!
}
}
Dentro desse mtodo, utilizamos o modelBuilder para definir os relacionamentos
entidade. Para definirmos que estamos mapeando a Categoria, utilizamos o
mtodo Entity:
modelBuilder.Entity<Categoria>()

da

E agora para definirmos que a categoria tem muitos produtos, utilizamos o


mtodo HasMany passando um lambda que devolve qual o atributo da categoria que
representa o relacionamento:
modelBuilder.Entity<Categoria>()
.HasMany(categoria => categoria.Produtos)

Agora para indicarmos que o relacionamento da propriedade Produtos a outra ponta


da propriedade Categoria do produto, utilizamos o mtodo WithOptional. Nesse
mtodo precisamos passar um lambda que devolver a categoria do produto:
modelBuilder.Entity<Categoria>()
.HasMany(categoria => categoria.Produtos)
.WithOptional(produto => produto.Categoria);

Com isso, o entity framework sabe as duas pontas do relacionamento definido.


Vamos agora testar tentar listar todas as produtos de uma determinada categoria, para
isso, buscaremos a categoria do banco de dados e depois utilizaremos o foreach para
imprimir todos os produtos:
static void Main(string [] args)
{
var contexto = new EntidadesContext();
Categoria categoria = contexto.Categorias.Find(1L);
foreach(var produto in categoria.Produtos)
{
Console.WriteLine(produto.Nome);
}
contexto.Dispose();
}

Com esse cdigo simples, conseguimos mapear os relacionamentos das entidades.

Consistncia de relacionamentos
bidirecionais

Imagine que temos a seguinte categoria no banco de dados:


+----+--------------+
| Id | Nome
|
+----+--------------+
| 1 | Informtica |
+----+--------------+

Associados a essa categoria, temos os seguintes produtos:


+----+---------+--------+-------------+
| Id | Nome | Preco | CategoriaId |
+----+---------+--------+-------------+
| 1 | Teclado | 20.00 |
1|
| 2 | Monitor | 300.00 |
1|
+----+---------+--------+-------------+

Podemos recuperar a lista de produtos utilizando o cdigo abaixo:


var context = new EntidadesContext();
Categoria categoria = context.Categorias.Find(1L);
IList<Produto> produtos = categoria.Produtos;

Quando executamos esse cdigo, o Entity Framework executa uma query que recupera
apenas a categoria do banco de dados, os produtos relacionados a essa categoria, por
padro, no so carregados.
O Entity Framework carrega os relacionamentos apenas quando necessrio (modo lazy).
Quando pedimos qualquer informao sobre o relacionamento, ele forado a realizar a
busca no banco de dados:
var context = new EntidadesContext();
Categoria categoria = context.Categorias.Find(1L);
IList<Produto> produtos = categoria.Produtos;
Console.WriteLine(produtos.Count);

Esse cdigo imprime 2 no terminal, pois no banco de exemplo temos 2 produtos


associados categoria de id 1. Agora vamos adicionar mais um produto com a categoria
1 e imprimir novamente o nmero de produtos dessa categoria:
var context = new EntidadesContext();
Categoria categoria = context.Categorias.Find(1L);
IList<Produto> produtos = categoria.Produtos;
Console.WriteLine(produtos.Count);
Produto produto = new Produto()
{
Categoria = categoria,
Nome = "nome",
Preco = 200.0
};
context.Produtos.Add(produto);
Console.WriteLine(categoria.Produtos.Count);

Nesse cdigo, o primeiro WriteLine continua imprimindo o nmero 2 no terminal e


depois de adicionarmos o novo produto no contexto, o Console.WriteLine imprime o
nmero 3, ou seja, o entity framework consegue perceber mudanas no contexto e
sincronizar as pontas do relacionamento!

Agora que j vimos como mapear as entidades e seus relacionamentos, vamos aprender
como escrever consultas no banco de dados com o Entity Framework.
Como vimos anteriormente, a comunicao com o banco de dados feita atravs
do DbContext e toda vez que queremos fazer uma query, precisamos utilizar os
conjuntos de entidades que foram mapeados no contexto:
EntidadesContext contexto = new EntidadesContext();

Assim como quando estamos trabalhando com listas e conjuntos do C#, fazemos buscas
nos conjuntos do contexto utilizando a Language Integrated Query ou LINQ. Ento para
listarmos todos os produtos do banco de dados, utilizamos o seguinte cdigo:
var busca = from p in contexto.Produtos select p;

Agora para transformarmos o resultado da query em uma lista de produtos, precisamos


apenas chamar o mtodo ToList na busca:
var busca = from p in contexto.Produtos select p;
IList<Produto> produtos = busca.ToList();

A query definida na varivel busca s enviada para o banco de dados quando


executamos o mtodoToList.
E agora, podemos, por exemplo, utilizar o foreach para imprimir os produtos que foram
recuperados do banco de dados:
var busca = from p in contexto.Produtos select p;
IList<Produto> produtos = busca.ToList();
foreach(var produto in produtos)
{
Console.WriteLine(produto.Nome);
}

E se quisermos ordenar o resultado por algum critrio, podemos utilizar o orderby do


LINQ:
var busca = from p in contexto.Produtos orderby p.Nome select p;

Buscando produtos por preo


Agora que j aprendemos como fazer uma query que lista entidades, vamos aprender
como colocar restries na busca. Assim como na busca em listas do c#, utilizamos a
instruo where do LINQ:
var busca = from p in contexto.Produtos
where condies
select p;

Queremos buscar todos os produtos com preo maior do que 10.0, ento a condio da
query deve aceitar apenas produtos com a propriedade Preco maior do que o valor 10.0:
var busca = from p in contexto.Produtos
where p.Preco > 10.0m
select p;

E se tambm quisermos os produtos com preo maior do que 100.0? Teramos que
escrever uma query diferente. Precisamos definir um parmetro nessa busca que ser o
preo mnimo do produto e como o LINQ se integra com o cdigo C#, podemos
simplesmente utilizar as variveis que esto em escopo dentro da busca:
decimal preco = 100.0;
var busca = from p in contexto.Produtos
where p.Preco > preco
select p;

Com isso, o Entity Framework define uma query com parmetros utilizando a SQL e j
substitui o valor do parmetro!

Busca de produtos por categoria


Queremos recuperar todos os produtos que pertencem a uma categoria chamada
informtica, porm a Categoria um outro modelo do sistema e representada por uma
tabela diferente no banco de dados.
Para realizarmos essa busca, utilizando a SQL, teramos que utilizar o join:
select p.*
from Produto p
inner join Categoria c on p.CategoriaId = c.Id
where c.Nome = 'Informatica'

Com o LINQ, precisamos apenas acessar as propriedades da entidade. Por exemplo,


para filtrarmos todos os produtos de uma categoria cujo nome est em uma varivel do
cdigo, utilizaramos a seguinte query:
string nomeCategoria = "Informatica";
var query = from p in contexto.Produtos
where p.Categoria.Nome == nomeCategoria
select p;

Repare que na query acima, no precisamos nos preocupar com o join entre as tabelas,
responsabilidade do Entity Framework enviar a SQL correta para o banco de dados.
Podemos tambm utilizar diversas condies na query. Assim como no if do C#,
podmeos utilizar o &&e o || para juntar as condies da query. Por exemplo, se
quisssemos todos os produtos cuja categoria tem um determinado nome e com preo
maior do que um valor mnimo, poderamos utilizar a seguinte query:
string nomeCategoria = "Informatica";
decimal precoMinimo = 100.0m;
var query = from p in contexto.Produtos
where p.Categoria.Nome = nomeCategoria and p.Preco > precoMinimo
select p;

Nmero de produtos por categoria


Agora que j vimos como a HQL funciona, vamos escrever uma query mais avanada.
Queremos recuperar o nmero de produtos agrupados por categoria. Para resolvermos
esse problema na SQL utilizaramos o group by.
select c.Id, count(p.Id)
from Categoria c inner join Produto p on (c.Id = p.Categoria_Id)
group by c.Id

Podemos fazer uma query equivalente utilizando o LINQ. Essa query pode comear
pela entidade Categoria:
var busca = from c in contexto.Categorias select c

Mas nessa busca precisamos devolver tanto a categoria quanto a quantidade de produtos
associados. Para recuperar o nmero de produtos de uma determinada categoria,
podemos utilizar o count da lista:

c.Produtos.Count

Colocando essa linha na query, teremos:


var busca = from c in contexto.Categorias select c, c.Produtos.Count

Mas a busca do LINQ s pode devolver um objeto, logo precisamos usar uma projeo:
var busca = from c in contexto.Categorias
select new { Categoria = c, NumeroDeProdutos = c.Produtos.Count };

Com isso estamos devolvendo um objeto annimo que contm um campo que guarda a
categoria e outro que guarda o nmero de produtos daquela categoria. E agora podemos
listar os resultados da query e utilizar um foreach para mostrar os resultados:
var busca = from c in contexto.Categorias
select new { Categoria = c, NumeroDeProdutos = c.Produtos.Count };
var resultados = busca.List();
foreach(var resultado in resultados)
{
Console.WriteLine(resultado.Categoria.Nome + " " + resultado.NumeroDeProdutos);
}

Mas s podemos utilizar o tipo annimo dentro do mtodo que o declara, ento
criaremos uma nova classe para guardar o resultado dessa query:
public class ProdutosPorCategoria
{
public Categoria Categoria { get; set; }
public int NumeroDeProdutos { get; set; }
}

Para fazermos a query devolver instncias de ProdutosPorCategoria ao invs de


instncias de objetos annimos, utilizaremos os initializers do c# para construir o
objeto:
var busca = from c in contexto.Categorias
select new ProdutosPorCategoria()
{
Categoria = c,
NumeroDeProdutos = c.Produtos.Count
};

E agora, quando listarmos o resultado da query, teremos uma lista


de ProdutosPorCategoria:
var busca = from c in contexto.Categorias
select new ProdutosPorCategoria()
{
Categoria = c,
NumeroDeProdutos = c.Produtos.Count
};
IList<ProdutosPorCategoria> resultado = busca.ToList();

Quando desenvolvemos software utilizando o Entity Framework, no precisamos nos


preocupar tanto com o banco de dados relacional, o que facilita muito o
desenvolvimento, mas devemos cuidar para que essas facilidades no acabem
prejudicando a performance do sistema.
Como aprendemos anteriormente, o Entity Framework carrega todos os
relacionamentos de forma lazy, ou seja, os relacionamentos so carregados apenas

quando necessrio. Imagine que em nossa loja, queremos imprimir a lista com o nome
de todos os produtos junto com o nome da categoria de cada produto.
var produtos = contexto.Produtos.ToList();
foreach(var produto in produtos)
{
Console.WriteLine(produto.Nome + " - " + produto.Categoria.Nome);
}

No cdigo acima, fazemos uma query para recuperar a lista de todos os produtos e
depois para cada produto imprimimos seu nome e o nome de sua categoria, porm a
categoria um relacionamento, ento o NHibernate s a carrega quando necessrio
(quando acessamos seu nome), ou seja, para cada produto estamos enviando uma query
para carregar sua categoria. Esse problema conhecido como N + 1 queries.

Evitando o problema do N+1


Quando estamos utilizando a SQL, podemos resolver o problema das N + 1 queries
utilizando um join:
select p.*, c.*
from Produto p join Categoria c on p.CategoriaId = c.Id

Podemos fazer algo parecido utilizando o LINQ. Para especificarmos que a query do
LINQ deve recuperar as categorias junto com a lista de produtos, utilizamos o mtodo
include do DbSetinformando qual relacionamento queremos carregar:
var busca = ctx.Produtos.Include("Categoria");
var produtos = busca.List();
Tambm podemos utilizar o Include na busca do LINQ:
var busca = from produto in contexto.Produtos.Include("Categoria") select produto;
var produtos = busca.List();

Agora que estamos utilizando o LINQ, a query para trazer a lista de produtos j
carregar os produtos de cada uma das categorias.

N + 1 em relacionamentos to many
Vamos agora pensar numa query que devolve a lista de categorias.
IList<Categoria> categorias = contexto.Categorias.ToList();

Para cada categoria queremos imprimir o tamanho de sua lista de produtos:


IList<Categoria> categorias = contexto.Categorias.ToList();
foreach(var categoria in categorias)
{
Console.WriteLine(categoria.Nome + " - " + categoria.Produtos.Count);
}

Como a lista de produtos um relacionamento do tipo one to many, ela carregada de


forma lazy pelo Entity Framework. Quando executamos categoria.Produtos.Count, o
Entity Framework executa uma query que busca os produtos relacionados a categoria.
Temos novamente o problema de N + 1 Queries.
Para resolver o problema de N + 1 queries no relacionamento to many, tambm
podemos utilizar oInclude:
var busca = contexto.Categorias.Include("Produtos");

Com isso, o Entity Framework traz a informao da categoria junto com sua lista de
produtos e com isso, conseguimos evitar o problema de N + 1 queries.

Queremos colocar na aplicao um novo relatrio de produtos. Esse relatrio filtrar os


produtos por nome, nome da categoria e preo mnimo, porm essas informaes so
opcionais.
Quando a o nome fornecido para a busca, devemos colocar a condio que compara o
nome do produto na busca, seno, devemos ignorar essa condio. Faremos o mesmo
para a categoria e o preo do produto.
Como essa busca acessa o banco de dados para procurar informaes sobre produtos,
colocaremos sua implementao dentro da classe ProdutosDAO.
public class ProdutosDAO
{
private EntidadesContext contexto;
// implementao dos outros mtodos do DAO.
public IList<Produto> BuscaPorNomePrecoMinimoECategoria(string nome,
double precoMinimo, string nomeCategoria)
{
}
}

Dentro desse mtodo do DAO, precisamos criar a query do LINQ que far essa busca,
mas como ficar essa query? Comearemos com uma query que lista todos os produtos
do banco de dados:
public IList<Produto> BuscaPorNomePrecoMinimoECategoria(string nome,
double precoMinimo, string nomeCategoria)
{
var busca = from produto in contexto.Produtos select produto;
}

Agora se o nome estiver definido, queremos comparar o nome do produto na condio


da query:
var busca = from produto in contexto.Produtos select produto;
if(!String.IsNullOrEmpty(nome))
{
// coloca a nova condio na query
}

Agora para colocar essa nova condio, precisamos continuar a query que foi iniciada
na varivel busca. Para isso podemos utilizar a busca dentro do LINQ como se fosse
uma lista!
var busca = from produto in contexto.Produtos select produto;
if(!String.IsNullOrEmpty(nome))
{
busca = from produto in busca where produto.Nome == nome select produto;
}

Esse cdigo funciona pois as queries do LINQ so enviadas para o banco de dados
apenas quando chamamos o mtodo ToList ou iteramos na busca, alm disso, toda vez

que utilizamos a varivelbusca em uma nova query, estamos adicionando novas


restries busca. Agora precisamos apenas completar os outros ifs da busca:
if(preco > 0.0m) {
busca = from produto in busca where produto.Preco > preco select produto;
}
if(!String.IsNullOrEmpty(nomeCategoria))
{
busca = from produto in busca where produto.Categoria.Nome == nomeCategoria;
}

Com isso conseguimos resolver o problema da busca dinmica. Agora s precisamos


listar os produtos da busca:
return busca.ToList();

O cdigo completo da soluo fica da seguinte forma:


public IList<Produto> BuscaPorNomePrecoMinimoECategoria(string nome,
double precoMinimo, string nomeCategoria)
{
var busca = from produto in contexto.Produtos select produto;
if(!String.IsNullOrEmpty(nome))
{
busca = from produto in busca where produto.Nome == nome select produto;
}
if(preco > 0.0m) {
busca = from produto in busca where produto.Preco > preco select produto;
}
if(!String.IsNullOrEmpty(nomeCategoria))
{
busca = from produto in busca where produto.Categoria.Nome == nomeCategoria;
}
return busca.ToList();
}

Busca dinmica com os mtodos do LINQ


Repare que na soluo da busca dinmica estamos a todo instante repetindo o cdigo:
from produto in contexto.Produtos where alguma condio select produto;

Para diminuir a repetio, podemos utilizar as chamadas de mtodo do LINQ. Toda vez
que queremos incluir uma nova restrio na query, podemos utilizar o mtodo Where.
busca = busca.Where(produto => condio)

Para utilizarmos a sintaxe de mtodos, o tipo da varivel busca deve ser IQueryable,
como DbSetimplementa IQueryable, podemos fazer:
IQueryable<Produto> busca = contexto.Produtos;

E agora podemos colocar a condio comparando o nome do produto com o seguinte


cdigo:
busca = busca.Where(produto => produto.Nome == nome);

Agora podemos reescrever as condies da busca com o seguinte cdigo:


if(!String.IsNullOrEmpty(nome))
{
busca = busca.Where(produto => produto.Nome == nome);
}

if(preco > 0.0m) {


busca = busca.Where(produto => produto.Preco > preco);
}
if(!String.IsNullOrEmpty(nomeCategoria))
{
busca = busca.Where(produto => produto.Categoria.Nome == nomeCategoria);
}

E o mtodo do DAO fica da seguinte forma:


public IList<Produto> BuscaPorNomePrecoMinimoECategoria(string nome,
double precoMinimo, string nomeCategoria)
{
IQueryable<Produto> busca = contexto.Produtos;
if(!String.IsNullOrEmpty(nome))
{
busca = busca.Where(produto => produto.Nome == nome);
}
if(preco > 0.0m) {
busca = busca.Where(produto => produto.Preco > preco);
}
if(!String.IsNullOrEmpty(nomeCategoria))
{
busca = busca.Where(produto => produto.Categoria.Nome == nomeCategoria);
}
return busca.ToList();
}

Agora que j aprendemos a fazer as operaes bsicas da loja, vamos implementar as


vendas.
Toda venda feita para um usurio (relacionamento many to one com o usurio)
public class Venda
{
public virtual int Id { get; set; }
public virtual Usuario Cliente { get; set; }
}

Cada venda possui diversos produtos e um produto pode participar de vrias vendas, o
que caracteriza um relacionamento many to many. Para representar o many to many,
colocaremos uma lista de produtos como propriedade da venda e faremos sua
inicializao no construtor da classe:
public class Venda
{
public virtual int Id { get; set; }
public virtual Usuario Cliente { get; set; }
public virtual IList<Produto> Produtos { get; set; }
public Venda()
{
this.Produtos = new List<Produto>();

}
}

Vamos agora mapear a venda dentro do contexto do Entity Framework:


public class EntidadesContext : DbContext
{
// outros mapeamentos
public DbSet<Venda> Vendas;
}

E agora precisamos configurar o contexto para que ele saiba que a lista de vendas um
relacionamento many-to-many. No banco de dados, para representarmos um
relacionamento many to many, utilizamos uma tabela intermediria que guarda os ids
das entidades participantes.
Para mepearmos a lista, precisamos abrir novamente o mtodo OnModelCreating:
public class EntidadesContext : DbContext
{
// mapeamentos
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// outras configuraes
modelBuilder.Entity<Venda>();
}
}

Para configurar que a venda possui uma lista de produtos, utilizamos o mtodo HasMany:
modelBuilder.Entity<Venda>()
.HasMany(x => x.Produtos);

Com isso mapeamos que esse relacionamento to many, para falarmos que ele many
to many, precisamos utilizar o WithMany:
modelBuilder.Entity<Venda>()
.HasMany(x => x.Produtos)
.WithMany();

Para definirmos qual ser a tabela de relacionamento, utilizamos o mtodo Map:


modelBuilder.Entity<Venda>()
.HasMany(x => x.Produtos)
.WithMany()
.Map(relacionamento => {
// configuraes do relacionamento
});

Dentro do mtodo Map, podemos configurar as caracteristicas do relacionamento


Many-to-Many. Para definirmos o nome da tabela de relacionamentos, utilizamos o
mtodo ToTable no relacionamento:
relacionamento.ToTable("Venda_Produtos");

Agora para definirmos os nomes das chaves estrangeiras, utilizamos os


mtodos MapLeftKey para mapear a chave estrangeira que aponta para
a Venda e MapRightKey para mapearmos a que aponta para o Produto:
relacionamento.MapLeftKey("VendaId");
relacionamento.MapRightKey("Produto_Id");

O mapeamento completo do relacionamento fica da seguinte forma:


modelBuilder.Entity<Venda>()
.HasMany(x => x.Produtos)
.WithMany()
.Map(relacionamento => {
relacionamento.ToTable("Venda_Produtos");
relacionamento.MapLeftKey("VendaId");

relacionamento.MapRightKey("Produto_Id");
});

Depois de configurarmos o novo modelo, precisamos criar uma migrao que atualizar
as tabelas do banco de dados. No console do NuGet, criaremos uma nova migrao com
o comando Add-Migration:
Add-Migration CriaVenda

Essa migrao criar a tabela de vendas e a tabela que guardar as informaes do


relacionamento many-to-many.
Agora precisamos apenas executar a migrao com o comando Update-Database no
console do NuGet.

Criando Vendas
Agora que conseguimos mapear os produtos e as vendas, vamos comear a vender
produtos para os clientes!
Quando vendemos um produto, precisamos inicialmente saber para qual cliente estamos
vendendo:
var contexto = new EntidadesContext();
Venda venda = new Venda();
Usuario cliente = contexto.Usuarios.Find(1L);
venda.Cliente = cliente;

Depois de definirmos para quem estamos vendendo, informaremos que o cliente


comprar os produtos com ids 1 e 2, por exemplo.
Produto p1 = contexto.Produtos.Find(1L);
Produto p2 = contexto.Produtos.Find(2L);
Para relacionar os produtos p1 e p2 com

a venda, precisamos apenas adicion-los na


lista de produtos, o Entity Framework cuidar da sincronizao com o banco de dados!
venda.Produtos.Add(p1);
venda.Produtos.Add(p2);

Agora que j criamos a venda, vamos adicion-la ao contexto e salvar as modificaes:


contexto.Vendas.Add(venda);
contexto.SaveChanges();

O cdigo completo para a criao da venda fica da seguinte forma:


var contexto = new EntidadesContext();
Venda venda = new Venda();
Usuario cliente = contexto.Usuarios.Find(1L);
venda.Cliente = cliente;
Produto p1 = contexto.Produtos.Find(1L);
Produto p2 = contexto.Produtos.Find(2L);
venda.Produtos.Add(p1);
venda.Produtos.Add(p2);
contexto.Vendas.Add(venda);
contexto.SaveChanges();

Venda para empresas

Nossa loja cresceu muito e agora atende tambm a revendedores, ou seja, ns


agora vendemos produtos no atacado e no varejo atendendo empresas e
cidados comuns, mas nosso modelo atual no atende essa nova regra de
trabalho. Precisamos fazer uma modificao para conseguir identificar se o
cliente pessoa fsica ou pessoa jurdica.
Pessoas fsicas possuem todos os atributos que definimos para a
entidade Usuario e um atributo chamado CPF.
public class PessoaFisica
{
public int Id { get; set; }
public string Nome { get; set; }
public string Senha { get; set; }
public string CPF { get; set; }
}

J as pessoas jurdicas possuem, alm dos atributos do Usuario, um


atributo CNPJ.
public class PessoaJuridica
{
public int Id { get; set; }
public string Nome { get; set; }
public string Senha { get; set; }
public string CNPJ { get; set; }
}

Esse modelo atende nossa loja, porm teremos de replicar todas as


funcionalidades do Usuario para essas duas novas entidades, o que dificulta a
manuteno do cdigo. Se precisarmos alterar a lgica do cliente,
precisaremos alterar para os dois tipos de cliente. Alm disso, para listarmos
todos os clientes da loja, precisaramos de dois selects, um na
entidade PessoaFisica e outro naPessoaJuridica.
Na orientao a objetos, quando queremos resolver esse tipo de problema,
utilizamos o Polimorfismo. Para aproveitarmos a implementao existente na
classe Usuario, faremos com que as
entidadesPessoaJuridica e PessoaFisica herdem da entidade Usuario.
Os modelos ficaro da seguinte forma:
public class PessoaFisica : Usuario

{
public string CPF { get; set; }
}

public class PessoaJuridica : Usuario


{
public string CNPJ { get; set; }
}

Como todos os clientes do sistema devem ser uma instncia ou


de PessoaFisica ou dePessoaJuridica, no queremos permitir que a
classe Usuario seja instanciada, ela, portanto, ser uma classe abstrata:
public abstract class Usuario
{
public virtual int Id { get; set; }
public virtual string Nome { get; set; }
}

Agora que j definimos as entidades, precisamos mape-las no Entity


Framework. Existem duas estratgias principais para o mapeamento de
Herana:

Tabela nica: O Entity Framework utilizar uma nica tabela que conter todas as propriedades
mapeadas pela classe pai ou por suas filhas.

Uma Tabela por subclasse: Nesse mapeamento, o Entity Framework cria uma tabela que
armazena os dados da classe pai e uma para para cada uma de suas filhas. As tabelas que
representam as classes filhas tem um relacionamente do tipo one to one com a tabela da classe
pai.

Tabela nica
O mapeamento da herana em uma nica tabela o padro do Entity
Framework.
Como estamos armazenando dados de vrias entidades em uma nica tabela,
precisamos diferenciar os registros de alguma forma. Para fazer essa
diferenciao, o Entity Framework precisa de uma coluna que discriminar o
tipo de entidade que est gravada naquele registro. Por padro o nome dessa

coluna especial Discriminator e o valor que ser armazenado ser o nome do


tipo guardado.
Como utilizaremos as configuraes padro, no precisamos customizar mais
nada no mapeamento, o Entity Framework far todo o trabalho sozinho.
Agora para conseguirmos utilizar a herana que acabamos de definir,
precisamos apenas criar uma nova migrao dentro do cdigo.
Ento no console do nugget vamos adicionar uma nova migrao utilizando o
Add-Migration:
Add-Migration HerancaEmTabelaUnica

Quando executarmos a migration, teremos o seguinte banco:

Por fim, vamos inserir dois usurios em nosso banco de dados, um do


tipo PessoaFisica e outro do tipo PessoaJuridica.
EntidadesContext contexto = new EntidadesContext();

PessoaFisica murilo = new PessoaFisica();


murilo.Nome = "Murilo";
murilo.Senha = "987";
murilo.CPF = "123.456.789.00";
contexto.Usuarios.Add(murilo);

PessoaJuridica caelum = new PessoaJuridica();


caelum.Nome = "Caelum";
caelum.Senha = "987";
caelum.CNPJ = "123.456/0001-09";

contexto.Usuarios.Add(caelum);

contexto.SaveChanges();

Com esse cdigo, o Entity Framework gravar as informaes tanto da pessoa


fsica quanto da pessoa jurdica dentro da tabela de usurios e preencher o
campo discriminador com o nome da classe.
Mas o que acontece com os dados que j estavam gravados no banco de
dados? Como os registros antigos no possuem um valor no campo
discriminador, o Entity Framework no considera essas linhas como resultados
vlidos da query e, portanto, elas no so listadas pela coleo de usurios do
contexto.
Quando trabalhamos com banco de dados, muitas vezes precisamos migrar os
dados antigos do banco para que eles sejam compatveis com a aplicao. No
Entity Framework, podemos resolver esse problema atravs das migrations!
Vamos adicionar uma nova migrao para atualizar a coluna Discriminator do
Usurio.
Add-Migration MigraDadosDoUsuario

Quando executarmos esse comando no console do NuGet, o Entity Framework


criar uma classe de migrao que ter os mtodos Up e Down com
implementaes vazias.
public partial class MigraDadosDoUsuario : DbMigration
{
public override void Up()
{
}

public override void Down()


{
}
}

No mtodo Up, queremos executar uma SQL que vai atualizar as informaes
do banco de dados, fazemos isso com o mtodo Sql da classe DbMigration:
public partial class MigraDadosDoUsuario : DbMigration

{
public override void Up()
{
Sql("sql que eu quero executar no banco");
}
public override void Down() { }
}

A Sql que queremos executar deve colocar o valor 'PessoaFisica'


em Discriminator se a coluna estiver vazia.
update tbl_Usuarios set Discriminator='PessoaFisica' where Discriminator=''

Onde tbl_Usuarios o nome da tabela que guarda as informaes dos


usurios. Agora s precisamos colocar essa Sql dentro do mtodo Sql da
migration:
public partial class MigraDadosDoUsuario : DbMigration
{
public override void Up()
{
Sql("update tbl_Usuarios set Discriminator='PessoaFisica' where Discriminator=''");
}
public override void Down() { }
}

E depois de executarmos o comando Update-Database, nossos dados estaro


atualizados.

Uma tabela por subclasse


Quando queremos utilizar uma tabela por subclasse, precisamos mapear
explicitamente as entidades que pertencem hierarquia de classes
explicitamente dentro do mtodo OnModelCreating doEntidadesContext:
public class EntidadesContext : DbContext
{
public DbSet<Usuario> Usuarios { get; set; }
// outras propriedades

public override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder.Entity<PessoaFisica>().ToTable("PessoaFisica");

modelBuilder.Entity<PessoaJuridica>().ToTable("PessoaJuridica");
}
}

Agora para testarmos esse mapeamento, precisamos novamente fazer a


migrao do banco de dados, porm nosso banco j est com o primeiro
mapeamento de herana aplicado. Ento antes de gerarmos a migrao para o
novo mapeamento, precisamos desfazer a migrao anterior, voltando para a
ltima migrao antes do mapeamento da herana, que a migrao do ManyTo-Many. Para isso, utilizaremos novamente o comando UpdateDatabase

informando para qual migrao queremos ir atravs da opo -

TargetMigration:
Update-Database -TargetMigration:CriaVenda

Com isso, o Entity Framework executar o mtodo Down das


migraes MigraDadosDoUsuario eHerancaEmTabelaUnica.
Agora vamos apagar as migraes MigraDadosDoUsuario e
HerancaEmTabelaUnica e criar a migrao para o novo mapeamento da
Herana:
Add-Migration HerancaTabelaPorClasse
Update-Database

Quando inserirmos novamente a pessoa fsica, o Entity Framework executar


um insert na tabelaUsuario e um na tabela PessoaJuridica. O mesmo acontecer
quando inserirmos a pessoa jurdica.

You might also like