RSS feed
Entity Framework 4 "Codificação Antecipada": Mapeamento Personalizado do Esquema do Banco de Dados - ScottGu's Blog em Português

Entity Framework 4 "Codificação Antecipada": Mapeamento Personalizado do Esquema do Banco de Dados


Na semana passada eu escrevi sobre a nova opção de "desenvolvimento antecipado de código" do Entity Framework 4. A opção de "desenvolvimento antecipado de código" do EF permite um fluxo de trabalho de desenvolvimento de código extremamente suave para trabalhar com dados. Esta opção de desenvolvimento lhe permite:
  • Desenvolver sem ter que abrir um designer ou definir um arquivo de mapeamento XML
  • Definir objetos do modelo, simplesmente escrevendo "classes padrão" do .NET, sem a necessidade de classes base
  • Usar uma abordagem de "convenção sobre configuração", que permite a persistência de dados sem configurar nada explicitamente

No post da última semana eu demonstrei como usar as convenções de mapeamento padrão do EF4 para permitir a persistência do banco de dados. Estas convenções padrão funcionam muito bem para novas aplicações, permitindo que você evite a configuração de qualquer coisa para mapear classes de/para um banco de dados. 

No post de hoje eu vou discutir como você pode substituir as regras de mapeamento de persistência padrão, passando a usar qualquer esquema de banco de dados personalizado que você deseje. Isso é particularmente útil em cenários que envolvem bancos de dados existentes (cujo esquema já está definido e, potencialmente, não pode ser alterado), bem como em situações onde você quer que a estrutura do seu modelo seja diferente da maneira como você deseja persistir o mesmo dentro de um banco de dados relacional.

Recapitulação Rápida do nosso Exemplo NerdDinner

No post da semana passada eu mostrei como construir uma simples aplicação chamada "NerdDinner" a partir do zero, e demonstrei os ganhos de produtividade que o paradigma de "desenvolvimento de código antecipado" do EF fornece quando se trabalha com dados. 

imagem

A seguir estão as duas classes do modelo que criamos para representar os dados dentro da aplicação. Elas são "simples objetos do CLR" (também conhecidos como "POCO") que só expõem tipos de dado padrão do .NET:

imagem

Criamos, então, uma classe "NerdDinners" para ajudar a mapear essas classes de/para um banco de dados.  "NerdDinners" deriva da classe DbContext provida pela biblioteca de "codificação antecipada" do EF e expõe duas propriedades públicas:

imagem

Utilizamos as convenções padrão da "codificação antecipada" do EF4 para permitir a persistência de dados para o banco de dados. Isto significa que as propriedades "Dinners" (jantares) e "RSVPs" (respostas dos convites) na nossa classe "NerdDinners" mapeiam para tabelas com os mesmos nomes no nosso banco de dados. Cada propriedade em nossas classes do modelo "Dinner" e "RSVP", por sua vez mapeiam para colunas dentro das tabelas "Dinners" e "RSVPs".

A seguir está a definição do esquema do banco de dados para a tabela "Dinners" dentro do nosso banco de dados:

imagem

A seguir está a definição do esquema do banco de dados para a tabela "RSVPs" dentro do nosso banco de dados:

imagem

Nós não tivemos que configurar nada a fim de termos esses mapeamentos de persistência do banco de dados com o "desenvolvimento antecipado de código" do EF4 - isso ocorre por padrão simplesmente escrevendo as três classes mostradas acima. Nenhuma configuração extra é necessária.

Ativando o Mapeamento de Persistência Personalizado/Customizado do Banco de Dados com o EF4

A abordagem de "desenvolvimento antecipado de código" permite que você, opcionalmente, substitua as regras de mapeamento de persistência padrão do banco de dados e configure meios alternativos para mapear suas classes para um banco de dados.

Existem algumas maneiras para habilitar isso. Uma das abordagens mais simples é sobrescrever o método "OnModelCreating" definido na classe base DbContext:

imagem

O método OnModelCreating mostrado acima será chamado na primeira vez em que nossa classe NerdDinners for usada dentro de uma aplicação em execução, e é passado para ele um objeto do tipo "Modelbuilder" como um argumento. O objeto ModelBuilder pode ser usado para personalizar as regras de mapeamento de persistência de banco de dados que os nossos objetos do modelo usam. Vamos olhar alguns exemplos de como fazer isso a seguir.

O EF chama o método "OnModelCreating" uma única vez dentro de uma aplicação em execução - e, então, automaticamente armazena em cache os resultados do Modelbuilder. Isso evita o impacto de performance causado pela criação do modelo a cada vez em que a classe NerdDinners é instanciada, e significa que você não necessita escrever qualquer lógica de cache personalizada para obter um excelente desempenho em suas aplicações.

Cenário 1: Personalizar o Nome de uma Tabela

Vamos agora olhar algumas maneiras com as quais podemos usar o método OnModelCreating para personalizar a persistência do banco de dados dos nossos modelos. Vamos começar olhando em um cenário bastante comum - onde queremos mapear uma classe do modelo para um esquema de banco de dados cujo os nomes das tabelas são diferentes dos nomes das classes que queremos mapear. 

Por exemplo, vamos supor que nossa base de dados utiliza um padrão onde um prefixo "tbl" é adicionado nos nomes das tabelas. E assim, em vez de "Dinners" temos uma tabela "tblDinners" no banco de dados:

imagem

Queremos ainda mapear nossa classe limpa do modelo "Dinners" para esta tabela "tblDinners" - e fazer isso sem ter que decorá-la com qualquer atributo de persistência de dados:

imagem

Podemos alcançar esse mapeamento de persistência personalizado substituindo o método "OnModelCreating" dentro da nossa classe de contexto NerdDinners especificando uma regra de mapeamento personalizada dentro dele assim:

imagem

O código dentro do nosso método OnModelCreating() acima usa um estilo que segue o padrão Fluent API (em InglIes) - um estilo de arquitetura de API, que emprega encadeamento de métodos para criar um código mais legível e fluído. Estamos usando o objeto ModelBuilder para indicar que queremos mapear a classe "Dinner" para a tabela "tblDinners".

Esse é todo o código que precisamos escrever. Agora nossa aplicação usará a tabela "tblDinners" ao vez da tabela "Dinners" sempre que ela consultar ou armazenar objetos do tipo Dinner. Nós não tivemos que atualizar nossas classes do modelo Dinner ou RSVP para conseguirmos implementar esta funcionalidade - elas vão continuar a ser objetos POCO sem nenhum conhecimento de persistência.

Experimentando a Alteração feita acima

Se você fez o download da aplicação NerdDinner completa a partir do meu post anterior, você pode modificá-la para incluir o método OnModelCreating personalizado mostrado acima e então re-executá-la para ver a persistência personalizada do banco de dados em ação.

Nós habilitamos o recurso de criação/recriação automática do banco de dados dentro da abordagem "apenas código" do EF no post anterior. Isto significa que quando você re-executar a aplicação NerdDinner imediatamente depois de fazer a mudança de código OnModelCreating() mostrada acima, você verá que o banco de dados SQL CE é atualizado passando a ter uma tabela "tblDinners" ao invés de uma tabela "Dinners". Isso ocorre porque o EF detectou que a estrutura do nosso modelo foi modificada, e assim re-criou o banco de dados para que o mesmo coincida com a nossa estrutura do modelo. Ele honrou nossa regra de mapeamento customizada definida no método OnModelCreating() quando o banco de dados foi atualizado - motivo pelo qual a tabela é agora "tblDinners" ao invés de "Dinners".

Várias pessoas me perguntaram no final do meu primeiro post se havia uma maneira de evitar que o EF automaticamente crie o banco de dados para você. Eu aparentemente não deixei claro o suficiente que a criação/recriação automática do banco de dados é uma opção que você deve ativar (e nem sempre acontece). Você pode sempre explicitamente criar seu banco de dados como quiser (usando código, script sql de deployment, uma ferramenta de administração SQL, etc) bastando apenas apontar sua string de conexão para o lugar correto - neste caso o EF nunca vai modificar ou criar o esquema de banco de dados.

Eu mostrei o recurso de criação automática do banco de dados no primeiro post principalmente porque acho que é um recurso útil para se aproveitar nas fases iniciais de um novo projeto. Ele definitivamente não é necessário, e muitas pessoas vão optar por não utilizá-lo.

É importante frisar que não precisamos mudar qualquer código dentro dos Controladores ou Visões da nossa aplicação ASP.NET MVC. Devido a nossa classe "Dinner" não ter sofrido alteração, os Controladores e Visões não foram afetados pela mudança na persistência do banco de dados.

Cenário 2: Personalizar o Mapeamento de Colunas/Propriedades

Vamos agora olhar para um outro cenário comum - onde queremos mapear uma classe do modelo para um esquema de banco de dados onde os nomes das tabelas e colunas são diferentes dos nomes das classes e propriedades que queremos mapear. 

Por exemplo, vamos supor que nossa tabela "tblDinners" contém colunas que são prefixadas com "col" - e cujos nomes também são todos diferentes daqueles usados em nossa classe Dinner:

imagem

Nós ainda queremos mapear nossa classe limpa do modelo "Dinner" para esta tabela "tblDinners" - e queremos fazer isso sem ter que decorá-la com qualquer atributo de persistência de dados:

imagem

Podemos conseguir essa persistência personalizada atualizando nosso método "OnModelCreating" para que este tenha uma regra de mapeamento um pouco mais rica da seguinte forma:

imagem

O código acima usa as mesmas chamadas fluentes dos métodos .MapSingleType() e .ToTable() que nós usamos no cenário anterior. A diferença é que agora também estamos especificando algumas regras de mapeamento de coluna adicionais para o método MapSingleType(). Estamos fazendo isso passando um objeto anônimo que associa os nomes das colunas da nossa tabela com as propriedades da nossa classe Dinner. 

O parâmetro dinner que estamos especificando com a expressão lambda é fortemente tipado - o que significa que você tem intellisense e verificação em tempo de compilação para as propriedades do objeto "dinner." dentro do editor de código VS. Você também recebe o suporte à refatoração dentro do Visual Studio - o que significa que quando você renomear uma das propriedades da classe Dinner - você pode usar o suporte à refatoração do Visual Studio para atualizar automaticamente suas regras de mapeamento dentro do contexto acima (sem a necessidade de fazer isso manualmente).

Cenário 3: Dividindo uma Tabela entre Múltiplos Tipos

Tabelas relacionais dentro de um banco de dados muitas vezes são estruturadas em uma forma diferente daquela que você deseja criar no seu modelo de classes orientado a objetos. O que pode ser armazenado como uma grande tabela dentro de um banco de dados é, por vezes, melhor expressado através de múltiplas classes relacionadas a partir de uma perspectiva puramente orientada a objetos - e muitas vezes você quer a capacidade de dividir ou particionar tabelas entre múltiplos objetos relacionados a uma única entidade.

Por exemplo, em vez de uma coluna "colAddr" única para o nosso endereço, vamos supor que a nossa tabela "tblDinners" no banco de dados utiliza várias colunas para representar o "endereço" do nosso evento:

imagem

Ao invés de considerarmos estas colunas de endereço como sendo 4 propriedades distintas em nossa classe do modelo "Dinner", nós podemos desejar encapsular estas propriedades dentro de uma classe "Address" fazendo com que nossa classe "Dinner" exponha uma propriedade do tipo Address da seguinte forma:

imagem

Observe acima como nós simplesmente definimos uma classe chamada "Address", que tem 4 propriedades públicas, e a classe "Dinner" que referencia ela simplesmente expõe uma propriedade pública "Address". Nossas classes do modelo são puramente POCO e não possuem nenhum conhecimento de persistência.

Podemos atualizar nosso método "OnModelCreating" para suportar o mapeamento dessa estrutura de classes hierarquizada para uma única tabela no banco de dados usando uma regra assim:

imagem

Observe como estamos usando a mesma abordagem de mapeamento que utilizamos no exemplo anterior - onde mapeamos nomes de colunas da tabela para propriedades fortemente tipadas no nosso objeto do modelo. Estamos simplesmente estendendo esta abordagem para suportar sub-propriedades complexas também. O único conceito novo acima é que estamos também chamando Modelbuilder.ComplexType<Address>() para registrar a nossa classe Address como um tipo que nós podemos usar em expressões de mapeamento.

Isso é tudo o que temos que escrever para permitir o particionamento de tabelas entre vários objetos.

Faça o Download da aplicação NerdDinner Atualizada com Regras de Persistência de Banco de Dados Customizadas

Você pode fazer o download de uma versão atualizada da aplicação NerdDinner aqui. Você precisará do VS 2010 (ou do Visual Web Developer 2010 Express que é gratuito).

Você deve fazer o download e instalar o SQL CE 4 em sua máquina para que a aplicação de exemplo acima funcione. Você pode baixar a biblioteca de Codificação-Antecipada do EF aqui. Nenhum destes downloads vai impactar o desempenho de sua máquina.

Resumo

O lançamento da funcionalidade "Code-First" fornece uma maneira bastante agradável e centrada em código para trabalhar com dados. Ela traz consigo muita produtividade, bem como muito poder. Espero que estes dois posts forneçam um vislumbre de algumas das possibilidades que ela proporciona.

Você pode fazer o download do lançamento da CTP4 do EF Code-First aqui. Para aprender ainda mais sobre a abordagem de "Codificação Antecipada do EF" verifique esses posts escritos pela equipe do ADO.NET (em Inglês):

Espero que ajude,

Scott

PS Além do blog, eu também estou agora utilizando o Twitter para atualizações rápidas e para compartilhar links. Siga-me em: twitter.com/ScottGu

 

Texto traduzido do post original por Leniel Macaferi.

Published Friday, July 23, 2010 11:36 AM by Leniel Macaferi

Comments

# re: Entity Framework 4 "Codificação Antecipada": Mapeamento Personalizado do Esquema do Banco de Dados

Thursday, March 24, 2011 11:34 PM by Rodrigo

Agora o Entity Framework está ficando bom! Excelente post!

No contexto acima Address é um VO. Tá certo que VO's podem ser complexos, mas na maioria das vezes VO's são simples. Seria interessante modelBuilder.ValueObjectType<Address>() ao invés de modelBuilder.ComplexType<Address>().