Entity Framework - Usando Fluent API para definir relacionamentos
Neste artigo eu vou mostrar como usar os recursos da Fluent API para realizar configurações e mapeamentos com o Entity Framework. |
Ao trabalhar com a abordagem Code First usando o Entity Framework o
comportamento padrão é mapear suas classes POCO para tabelas usando um
conjunto de convenções nativas do EF. Às vezes, no entanto, você não pode ou não
quer seguir essas convenções e precisa mapear entidades para algo diferente do
que as convenções ditam.
Existem duas maneiras de realizar a configuração e o mapeamento no EF quando
você não deseja seguir as convenções padrão: Data
Annotations e Fluent API.
1- Data Annotations - Utiliza atributos para realizar o
mapeamento e configuração;
2- Fluent API - Fornece mais funcionalidades que o Data Annotations
Na abordagem Code First a Fluent API é mais
acessada sobrescrevendo o método OnModelCreating no seu DbContext.
A Fluent API suporta os seguintes tipos de mapeamentos:
Mapeamento | Para o Banco de dados |
---|---|
Model-wide Mapping |
|
Entity Mapping |
|
Property Mapping |
|
Neste artigo eu vou mostrar como definir os relacionamentos usando a Fluent API.
Recursos usados:
Criando o projeto no Visual Studio 2013 Express for windows desktop
Abra o VS 2013 Express for windows desktop e clique em New Project;
Selecione a linguagem Visual Basic e o template Windows Forms Application;
Informe o nome EF_Fluent_API e clique no botão OK;
A seguir vamos incluir uma referência ao Entity Framework em nosso projeto.
No menu TOOLS clique em Nuget Package Manager -> Manage Nuget Package for Solutions;
A seguir localize o Entity Framework, e, após selecionar o pacote clique no botão Install;
Após concluir a instalação clique no botão Close.
Como o meu objetivo neste artigo é mostrar o mapeamento usando o Fluent API não vou usar apenas o projeto Windows Forms e não vou criar camadas para separação das responsabilidades.
No menu PROJECT clique em Add Class e informe o nome Cliente.cs;
A seguir inclua o código abaixo na classe Cliente:
using System.Collections.Generic;
namespace EF6_FluentAPI
{
public class Cliente
{
public int ClienteId { get; set; }
public string Nome { get; set; }
public string Endereco { get; set; }
public string Telefone { get; set; }
public string Cidade { get; set; }
public virtual ICollection<Pedido> Pedidos { get; set; }
}
}
|
Repita o procedimento acima e crie a classe Pedido.cs;
A seguir inclua o código abaixo na classe Pedido:
namespace EF6_FluentAPI
{
public class Pedido
{
public int PedidoId { get; set; }
public string Item { get; set; }
public int Quantidade { get; set; }
public int Preco { get; set; }
public int? ClienteId { get; set; }
public virtual Cliente Cliente { get; set; }
}
}
|
A classe
Cliente define a propriedade do tipo coleção para Pedido.
A classe Pedido define a propriedade Cliented a qual é a chave
estrangeira e a propriedade Cliente (também referida como propriedade de
Navegação) da classe Cliente. Nesta definição do modelo inferimos que
essas classes estão relacionadas.
Definimos assim 2 classes do nosso domínio pois o Entity Framework vai usar essa informação para gerar o mapeamento objeto relacional , criar o banco de dados e as tabelas para a nossa aplicação.
Esse recurso chama-se Code-First (o código primeiro) e indica que partimos das definições de nossas classes de domínio para gerar o banco de dados e as tabelas e o contexto com o mapeamento objeto relacional.
Vamos agora definir uma string de conexão no arquivo App.Config de forma a definir o banco de dados que iremos usar. Abra o arquivo App.Config e defina o seguinte código no arquivo:
<connectionStrings>
<add
name="VendasConnectionString"
connectionString="Data Source=.;Initial Catalog=Vendas;Integrated Security=SSPI"
providerName="System.Data.SqlClient"/>
</connectionStrings>
|
Agora vamos definir a nossa classe de contexto no projeto.
Agora no menu PROJECT clique em Add Class e informe o nome VendasContexto e inclua o código abaixo nesta classe:
using System.Data.Entity;
namespace EF6_FluentAPI
{
public class VendasContexto : DbContext
{
public DbSet<Cliente> Clientes { get; set; }
public DbSet<Pedido> Pedidos { get; set; }
public VendasContexto()
: base("name=VendasConnectionString")
{ }
}
}
|
A nossa classe VendasContexto herda de DbContext e define as propriedades Clientes e Pedidos com as quais temos acesso as tabelas do banco de dados.
O construtor da classe define o nome do banco de dados que será criado pelo Entity Framework.
Isso é tudo que você precisa para iniciar a persistência e a consulta aos dados. Mas podemos melhorar...
E vamos fazer isso usando o método OnModelCreating da classe DbContext.
Este método é chamado quando o modelo para o contexto derivado for inicializado. A implementação padrão deste método não faz nada, mas pode ser substituído em uma classe derivada de tal forma que o modelo pode ser configurado. E vamos usar a Fluent API para realizar a configuração de mapeamento entre as entidades.
Vamos então abrir a classe VendasContexto e sobrescrever o método OnModelCreating usando o código abaixo que faz o mapeamento para as tabelas e define o seu relacionamento:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Mapeamento para a tabela Cliente //S1: Chave Primária para a tabela Cliente modelBuilder.Entity<Cliente>().HasKey(c => c.ClienteId);
//S2: A chave Identity Key para ClienteId // tamanho máximo para as propriedades Nome,Endereco,Telefone e Cidade //Mapeamento para a tabela Pedido //S2: Uma chave identity para o PedidoId //S2: O tamanho máximo para o item //S3: A chave estrangeira para a tabela Pedido - ClienteId // A deleção em cascata a partir de Cliente para Pedidos |
Vamos entender o que foi feito:
modelBuilder.Entity<Pedido>().HasRequired(c => c.Cliente.WithMany(p => p.Pedidos).HasForeignKey(p => p.ClienteId);
modelBuilder.Entity<Order>()
.HasRequired(c => c.Customer)
.WithMany(o => o.Orders)
.HasForeignKey(o => o.CustomerId)
.WillCascadeOnDelete(true);
Estamos configurando a deleção em cascata usando o método WillCascadeOnDelete();
Para definir que uma propriedade é requerida usando o método IsRequired() e HasRequired() configura um relacionamento requerido a partir do tipo da entidade;
O método WithMany permite indicar que uma propriedade contém um relacionamento do tipo Muitos;
O método HasForeignKey é usado para indicar qual propriedade é a chave estrangeira;
Nota: Diferença entre os métodos
HasRequired e HasOptional
HasRequired
HasOptional |
Testando o Mapeamento
Para testar o mapeamento vamos definir um código no formulário form1.vb incluindo um controle Button - btnProcessar - no formulário e definindo o seguinte código no seu evento Click:
private void btnProcessar_Click(object sender, EventArgs e) { using (VendasContexto contexto = new VendasContexto()) { try { var cliente1 = new Cliente { Nome = "Macoratti", Endereco = "Rua Peru, 100", Telefone = "4555-6666", Cidade = "Lins" }; contexto.Clientes.Add(cliente1); contexto.SaveChanges(); MessageBox.Show("Cliente criado com sucesso."); } catch(Exception ex) { MessageBox.Show(ex.Message); } } } |
Agora vamos executar o projeto e clicar no botão de comando. Após receber a mensagem - Cliente criado com sucesso - podemos verificar se o banco de dados Vendas e as tabelas foram criadas.
Abrindo a janela DataBase Explorer no Visual Studio podemos localizar o banco de dados Vendas, as tabelas Clientes e Pedidoes criadas, bem como os dados do cliente criado no projeto. (Estamos vendo dois registros porque eu clique duas vezes no botão):
O
EF pluraliza o nome das tabelas por isso obtivemos o nome Pedidoes. Como
ele usa as regras da língua inglesa ele acrescenta 'es' ao nome Pedido.
Para evitar isso podemos remover esse comportamento definindo no método
OnModelCreating a seguinte linha de código:
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Nota : Você vai precisar do namespace System.Data.Entity.ModelConfiguration.Conventions;
Constatamos assim que o mapeamento realizado usando a Fluent API funcionou corretamente.
Pegue o projeto completo aqui: EF6_FluentAPI.zip (sem as referências)
Não vos esqueçais da hospitalidade, porque por ela alguns, não o sabendo,
hospedaram anjos.
Hebreus 13:2
Referências: