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.

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:

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:

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:

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

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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.