Mês
passado eu escrevi sobre a nova opção de desenvolvimento do EF 4
chamada "Code First" ou "Código em Primeiro Lugar" em uma tradução
literal. A biblioteca EF "code-first" permite um fluxo de trabalho de desenvolvimento de código extremamente suave para trabalhar com dados. Esta opção de desenvolvimento te permite:
- Trabalhar com dados 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 meu post inicial,
apresentei a EF "code-first" e demonstrei como usar as convenções de
mapeamento padrão do EF4 para criar um novo 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
para/a partir de um banco de dados. Então escrevi um segundo post intitulado Mapeamento Personalizado do Esquema do Banco de Dados
que discutiu como você pode substituir as regras de mapeamento de
persistência padrão, permitindo esquemas de banco de dados
personalizados.
No
post de hoje cobrirei uma questão que várias pessoas me
perguntaram recentemente, que é: "como faço para usar a EF code-first
com um banco de dados existente?"
Usando a EF Code-First com um Banco de Dados Existente
A
EF "Code First" funciona muito bem com bancos de dados existentes, e
permite uma abordagem muito boa para desenvolvimento centrada em código
com bancos de dados. Em especial,
ela permite a utilização de "plain old classes" (também conhecidas como
POCO) para os objetos de seu modelo, e de maneira limpa mapeia os mesmos
de/para o banco de dados usando as convenções de mapeamento padrão ou
substituindo-as com regras de mapeamento personalizadas para o esquema do banco de dados.
A seguir estão instruções passo a passo de como você pode usar a EF "Code First" com um banco de dados existente.
Passo 1: Criar um novo Projeto ASP.NET Web Application
Vamos começar criando um novo projeto ASP.NET Web Application. Meus dois posts anteriores sobre a EF "code first" usaram ASP.NET MVC - neste post eu vou usar um projeto ASP.NET Web Forms. Note que todos os conceitos do EF são idênticos, independentemente de qualquer tipo de aplicação ASP.NET que você usar.
Nós
vamos usar o menu "File->New Project" (Arquivo->Novo Projeto)
dentro do VS 2010 (ou do Visual Web Developer 2010 Express - que é
grátis) e escolheremos o modelo de projeto "ASP.NET Web Application"
para criar uma nova aplicação.
O
novo projeto "ASP.NET Web Application" no VS 2010 é um bom modelo
inicial que fornece um layout com master-page (página-mestra) com design
baseado em CSS (eu escrevi sobre isso em um post anterior intitulado Modelos de Projeto Iniciais). Quando a aplicação for criada, você verá que ela contém alguns arquivos padrão dentro dela:
Nós
não precisamos desses arquivos padrão (poderíamos simplesmente usar o
modelo de projeto vazio chamado "Empty ASP.NET Web Application") - mas
esses arquivos farão com que nossa simples aplicação pareça um pouco
mais bonita, por padrão, então vamos usá-los.
Passo 2: Referenciar o Assembly (DLL) da EF Code First
Nosso próximo passo será adicionar uma referência à biblioteca EF Code First em nosso projeto. Clique
com botão direito do mouse no nó "References" (Referências) dentro do
Solution Explorer e escolha "Add Reference" (Adicionar Referência).
Você vai referenciar o assembly "Microsoft.Data.Entity.Ctp.dll"
que está instalado dentro da pasta "\Program Files\Microsoft ADO.NET
Entity Framework Feature CTP4\Binaries\" que é criado quando você faz o
download e instala a biblioteca EF Code First. Após adicionar essa referência você verá que ela aparece na janela de referências do seu projeto como a seguir:
Passo 3: Banco de Dados Northwind
Você pode ignorar esse passo se você tiver um banco de dados SQL Server com o Northwind (ou outro banco de dados) instalado.
Se você não tem o Northwind já instalado, então você pode baixá-lo aqui. Você pode usar os arquivos .SQL
inclusos para instalá-lo em um banco de dados SQL, ou pode copiar o
arquivo Northwind.mdf do SQL Express para o diretório \App_Data da sua
aplicação:
Etapa 4: Criar nossa Camada do Modelo
Agora
vamos escrever nossas classes do modelo e usaremos a EF "Code First"
para mapeá-las para o nosso banco de dados Northwind. A seguir está todo o código que precisamos escrever - nenhum outro código é necessário:
A seguir estão alguns detalhes sobre o que todo este código faz e como ele funciona:
Classes POCO do Modelo
A
EF "code first" nos permite usar "objetos simples da CLR" (também
conhecidos como POCO) para representar entidades dentro de um banco de
dados. Isso significa que nós não
temos que derivar nossas classes do modelo a partir de uma classe base, nem
devemos implementar qualquer interface ou atributos nelas. Isso nos permite manter nossas classes do modelo limpas e "ignorantes quanto à persistência".
Acima
nós definimos duas classes POCO - "Product" (Produto) e "Category"
(Categoria) - que usaremos para representar as tabelas "Products" e
"Categories" do nosso banco de dados Northwind. As propriedades nestas duas classes mapeiam para colunas dentro das tabelas. Cada instância de uma classe Product ou Category representa uma linha dentro da respectiva tabela no banco de dados.
Colunas Anuláveis (Nullable)
Observe que algumas das propriedades dentro da classe "Product" são definidas como nullable (isto é o que Decimal? significa - indica que ele é um tipo anulável). Colunas
anuláveis (nullable) dentro de uma tabela do banco de dados devem ser
representadas dentro da classe do modelo como propriedades Nullable se
elas são tipos de valor:
Você
também pode, opcionalmente, omitir completamente a especificação de
colunas anuláveis de uma classe do modelo, se você não precisar
acessá-las. Por exemplo, a tabela
Products dentro do banco de dados Northwind tem uma coluna
"QuantityPerUnit" (QuantidadePorUnidade) que é do tipo nvarchar
anulável, e uma coluna "UnitsOnOrder" (UnidadesNoPedido) que é do tipo
smallint anulável. Eu omiti essas duas propriedades da classe "Product" que defini acima. Porque elas são anuláveis no banco de dados ainda posso recuperar, inserir, atualizar e excluir produtos sem problemas.
Propriedades da Associação e Lazy Loading (Carregamento Tardio/Sob Demanda)
A
EF "code first" torna mais fácil tirar vantagem de relações do tipo
primary-key (PK) / foreign-key (FK) - chave primária/chave estrangeira -
dentro de um banco de dados, e expor propriedades em nossas classes do
modelo que nos permitem percorrer entre as classes do modelo usando as
mesmas.
Acima expusemos uma propriedade "Category" em nossa classe Product, e uma propriedade "Products" em nossa classe Category. Acessar estas propriedades nos permite usar o relacionamento PK/FK entre as duas tabelas para recuperar instâncias do modelo. Observe
como as próprias propriedades ainda são propriedades "POCO" e não nos
obrigam a usar qualquer tipo de coleção específico do EF para
definí-las.
Propriedades
de associação que são marcadas como "virtual" serão por padrão
carregadas tardiamente ou sob demanda (lazy loaded). O
que isto significa é que se você recuperar uma entidade do tipo
Product, as informações de sua categoria (Category) não serão
recuperadas do banco de dados até que você acesse sua propriedade
Category (a menos que você explicitamente indique que os dados da
categoria devem ser recuperados quando você escrever sua consulta LINQ
para recuperar o objeto do tipo Product).
Classe de Contexto do EF
Depois
de criarmos nossas classes POCO "Product" e "Category", nós
usamos a EF "code first" para criarmos uma classe "context" que podemos
usar para mapear nossas classes POCO do modelo de/para tabelas dentro do
banco de dados:
A
classe "Northwind" acima é a classe de contexto que estamos usando para
mapear nossas classes Product e Category de/para o banco de dados. Ela
é derivada da classe base DbContext fornecida pela EF "code first", e
expõe duas propriedades que correspondem às tabelas dentro do nosso
banco de dados. Para este exemplo
estamos usando as regras de mapeamento padrão de "convenção sobre
configuração" para definir como as classes devem mapear de/para o banco
de dados.
Poderíamos alternativamente substituir o método "OnModelCreating" e especificar as regras de mapeamento customizadas, se quiséssemos que o objeto do modelo tivesse um formato diferente do esquema do banco de dados. Meu post anterior sobre a EF "code first" explica como fazer isso.
Passo 5: Configurando a String de Conexão do nosso Banco de Dados
Nós escrevemos todo o código que precisamos escrever para definir nossa camada do modelo. Nosso último passo antes de usá-la será configurar uma string de conexão que conecta a mesma em nosso banco de dados.
No meu post inicial sobre a EF "code first"
discuti uma opção legal que a EF "code first" provê que permite que
você a utilize para auto-criar/recriar seu esquema do banco de dados
para você. Esta é uma opção que pode ser particularmente útil em cenários de desenvolvimento do tipo gree-field (campo-verde)
(em Inglês) - já que ela te permite focar na sua camada do modelo no início do
projeto sem que você tenha que gastar tempo com a atualização do esquema
do banco de dados após cada mudança no modelo.
Importante,
porém, a opção para criar o banco de dados automaticamente é apenas uma
opção - que definitivamente não é necessária. Se você apontar sua string de conexão para um banco de dados existente, a EF "code first" não irá tentar criar um banco de dados automaticamente. A
opção auto-recreate (recriar automaticamente) também não será ativada a
menos que você queira que o EF faça isso - assim você não precisa se
preocupar com a exclusão e recriação do seu banco de dados a menos que
você explicitamente tenha indicado que você quer que isto aconteça.
Para este post não iremos criar automaticamente o banco de dados. Em vez disso, vamos apontar para o banco de dados Northwind existente que já temos. Para isso vamos acrescentar uma string de conexão do "Northwind" em nosso arquivo web.config assim:
<connectionStrings>
<add name="Northwind"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\northwind.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
A EF "code first" usa uma convenção onde classes de contexto por padrão buscam por uma string de conexão que tenha o mesmo nome da classe do contexto. Devido nossa classe de contexto ser chamada "Northwind", ela por padrão procurará por uma string de conexão "Northwind" para usar. Acima, nossa string de conexão do Northwind é configurada para usar um banco de dados SQL Express local. Você pode, alternativamente, apontar para um SQL Server remoto.
Passo 6: Usando nossas Classes do Modelo
Vamos
agora escrever uma página (muito) simples que usa nossas classes do
modelo Northwind para mostrar alguns dados a partir do nosso banco de
dados.
Vamos começar adicionando uma nova página em nosso projeto ASP.NET. Clique
com o botão direito do mouse no projeto web e escolha Add->New Item
(Adicionar->Novo Item), e então selecione o item modelo "Web Form
using Master Page". Chamaremos a
página de "Products.aspx" e faremos com que ela use a página-mestre
"Site.master" que é incluída por padrão no modelo ASP.NET Web Project.
Vamos adicionar um controle <asp:GridView> em nossa nova página Products.aspx. Vamos configurá-lo para mostrar somente o nome e o preço dos nossos produtos:
Dentro
do nosso arquivo de code-behind podemos então escrever a seguinte
consulta LINQ tendo em vista nossas classes do modelo para recuperar
todos os produtos ativos do nosso banco de dados, e vinculá-los ao
controle GridView acima:
E agora, quando executamos o projeto e navegamos até a página Products.aspx vamos obter uma lista dos nossos produtos assim:
Temos agora uma aplicação simples que usa a EF "code first" e um banco de dados existente.
Download do Exemplo
Você pode baixar uma versão completa do exemplo acima aqui. Ele assume que você tem a EF "code first" CTP4 e o SQL Express instalado.
Exemplos de Código Adicionais
A
seguir estão alguns exemplos de código adicionais que demonstram como
podemos usar o nosso modelo Northwind para outros cenários comuns.
Consulta Através de Relações/Relacionamentos
A
consulta LINQ a seguir demonstra como recuperar uma sequência de
objetos de produto com base no nome da categoria a que pertencem. Observe
a seguir como podemos escrever consultas LINQ que abrangem tanto o
produto e uma sub-propriedade de seu objeto Category associado. A
filtragem real é totalmente feita no próprio servidor de banco de dados
- assim, somente objetos Product são retornados para a camada
intermediária (tornando-a eficiente):
Use o método Find para recuperar um Produto único
Além
de permitir que você escreva consultas LINQ, a EF "Code First" também
suporta um método "Find()" em coleções do tipo DbSet<T> que te
permite escrever código como o mostrado a seguir para obter uma única
instância com base na sua identificação (ID):
Inserindo uma Nova Categoria
O código a seguir demonstra como adicionar uma nova categoria no banco de dados:
Observe
como criamos o objeto Category, atribuímos propriedades a ele, e em
seguida, adicionamos o mesmo na coleção de categorias do contexto. Chamamos então SaveChanges() (Salvar Mudanças) no contexto para persistir as atualizações no banco de dados.
Inserindo uma nova Categoria e um novo Produto (e associando os mesmos)
O
código a seguir demonstra como criar uma nova categoria e um novo
produto, associar o produto de forma que ele pertença à nova categoria, e
depois salvar os dois objetos no banco de dados:
Observe
acima como somos capazes de fazer com que o novo produto referencie a
nova categoria fazendo com que sua propriedade "Category" aponte para a
instância da categoria. Nós não
precisamos definir explicitamente a propriedade de chave estrangeira
CategoryID - isto será feito automaticamente para nós quando
persistirmos as alterações para o banco de dados.
A EF code first usa um padrão chamado "unit of work" ou "unidade de trabalho" - o
que significa que ela pode rastrear várias mudanças em um contexto, e
depois quando "SaveChanges()" for chamado, ela pode persistir todas as mudanças de uma só vez em uma única transação atômica (o que significa
que todas as mudanças são realizadas com sucesso ou nenhuma delas é
persistida no banco de dados). Isto
torna mais fácil garantir que seu banco de dados não possa ser deixado
em um estado inconsistente - onde algumas alterações são aplicadas e
outras não.
No
trecho de código acima tanto a categoria quanto o produto serão ambos
persistidos (salvos), ou nenhum deles (e uma exceção será lançada).
Atualizando um Produto e Salvando o mesmo
O código a seguir demonstra como recuperar e atualizar um produto, e depois salvá-lo de volta no banco de dados. Anteriormente eu demonstrei como usar o método Find() para recuperar um produto baseado em sua identificação ProductID. A
seguir nós estamos usando uma consulta LINQ para recuperar um
produto específico com base na sua propriedade ProductName (Nome do
Produto).
Nós poderíamos fazer qualquer número de mudanças (para quaisquer objetos existentes, bem como adicionar novos). Quando chamamos SaveChanges() todos eles serão persistidos em uma única transação no banco de dados.
Convenções padrão versus Regras de Mapeamento Personalizadas
Quando
criamos as classes Product e Category anteriormente, usamos as
convenções padrão da EF "Code-First" para mapear as classes de/para o
banco de dados. Isso evitou que
precisássemos especificar qualquer regra de mapeamento personalizada, e
manteve o nosso código realmente conciso.
Existirá
certamente momentos em que você não gostará do formato do banco de
dados que você está mapeamento, e você desejará que o modelo de objetos
do seu modelo seja diferente. Veja meu post sobre Mapeamento Personalizado do Esquema do Banco de Dados para exemplos de como usar o EF para especificar regras de mapeamento personalizadas. Elas funcionam igualmente bem quando você faz o mapeamento de bancos de dados existentes.
Resumo
Estou
bastante animado com a funcionalidade da EF "Code-First" e acho que ela
oferece uma maneira bastante agradável e centrada em código para se
trabalhar com dados. Ela traz consigo uma série de recursos de produtividade, bem como muito poder para o desenvolvimento. Em
particular, eu gosto dela porque ela ajuda a manter o código realmente
limpo, sustentável, e te permite fazer muito de uma maneira concisa. Espero
que estes três últimos posts sobre a EF Code-First tenha dado uma idéia
sobre algumas das possibilidades que ela oferece - tanto para bancos de
dados novos quanto para existentes.
Você pode baixar a versão CTP4 da EF Code-First aqui. Para aprender ainda mais sobre a EF "Code First" 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.