Usando a Biblioteca EF "Code First" com um Banco de Dados Existente


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:

imagem

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:

imagem

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:

imagem

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:

imagem

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:

imagem

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:

imagem

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:

imagem

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: 

imagem 

E agora, quando executamos o projeto e navegamos até a página Products.aspx vamos obter uma lista dos nossos produtos assim:

imagem

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):

imagem

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):

imagem

Inserindo uma Nova Categoria

O código a seguir demonstra como adicionar uma nova categoria no banco de dados:

imagem

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:

imagem

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).

imagem

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.

No Comments