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.
- Data Annotations – Utiliza atributos para realizar o mapeamento e configuração;
- 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. Depois selecione a linguagem Visual Basic e o template Windows Forms Application, informando, em seguida, 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. Depois 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 classeCliente. Nesta definição do modelo inferimos que essas classes estão relacionadas.
Definimos assim duas classes do nosso domínio, pois o Entity Framework vai usar essa informação para gerar o mapeamento do objeto relacional, criar o banco de dados e as tabelas para a nossa aplicação.
Esse recurso se chama 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 do 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. Já 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
modelBuilder.Entity<Cliente>().Property(c => c.ClienteId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// tamanho máximo para as propriedades Nome,Endereco,Telefone e Cidade
modelBuilder.Entity<Cliente>().Property(c => c.Nome).HasMaxLength(80);
modelBuilder.Entity<Cliente>().Property(c => c.Endereco).HasMaxLength(100);
modelBuilder.Entity<Cliente>().Property(c => c.Telefone).HasMaxLength(20);
modelBuilder.Entity<Cliente>().Property(c => c.Cidade).HasMaxLength(50);
//Mapeamento para a tabela Pedido
//S1: Chave Primaria para a tabela Pedido
modelBuilder.Entity<Pedido>().HasKey(p => p.PedidoId);
//S2: Uma chave identity para o PedidoId
modelBuilder.Entity<Pedido>().Property(p => p.PedidoId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
//S2: O tamanho máximo para o item
modelBuilder.Entity<Pedido>().Property(p => p.Item).HasMaxLength(50);
//S3: A chave estrangeira para a tabela Pedido - ClienteId
modelBuilder.Entity<Pedido>().HasRequired(c => c.Cliente)
.WithMany(p => p.Pedidos).HasForeignKey(p => p.ClienteId);
// A deleção em cascata a partir de Cliente para Pedidos
modelBuilder.Entity<Pedido>()
.HasRequired(c => c.Cliente)
.WithMany(p => p.Pedidos)
.HasForeignKey(p => p.ClienteId)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
Vamos entender o que foi feito:
- O método OnModelCreating aceita o objeto DbModelBuilder como parâmetro. Esta classe é usada para mapear as classes CLR para o esquema do banco de dados;
- A implementação deste método define a chave Primária usando o método HasKey();
- O método HasDatabaseGenerationOption é usado para definir a chave do tipo identidade;
- O método HasMaxLenght() é usado para definir o tamanho para as colunas do tipo String;
- O relacionamento da chave estrangeira é definido entre Cliente e Pedido usando a expressão abaixo:
modelBuilder.Entity<Pedido>().HasRequired(c => c.Cliente.WithMany(p => p.Pedidos).HasForeignKey(p => p.ClienteId);
- O relacionamento um-para-muitos entre as duas tabelas é definida conforme a expressão abaixo:
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
Configura uma relação necessária com este tipo de entidade. Instâncias do tipo da entidade não serão capazes de serem salvas no banco de dados, a menos que essa relação seja especificada. A chave estrangeira no banco de dados será não-anulável.
HasOptional
Configura um relacionamento opcional a partir deste tipo de entidade. Instâncias do tipo da entidade serão capaz de serem salvas no banco de dados sem que esta relação tenha sido especificada. A chave estrangeira no banco de dados será anulável.
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 dadosVendas 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)







