Praticamente todo sistema tem a necessidade de persistir seus dados para posterior recuperação e estes dados são normalmente armazenados em bases de dados relacionais. Para realizar a persistência e acesso a estes, nós podemos utilizar a infraestrutura de persistência do ADO.NET ou como alternativa um framework de mapeamento objeto relacional, onde obtemos maior produtividade no desenvolvimento de nossas aplicações.


Guia do artigo de Entity Framework:

Este artigo fala sobre conceitos fundamentais do Entity Framework, da Microsoft.

Primeiros passos no Entity Framework

O Entity permite que façamos um mapeamento dos elementos de nossa base de dados para os elementos de nossa aplicação orientada a objetos, possuindo três linhas principais de utilização: Database First, Model First e Code First. Ao longo deste artigo apresentaremos os principais conceitos do EF onde abordaremos estas três linhas de utilização, conhecendo as características de cada uma.

O uso de ORMs (Object-Relational Mappers) auxilia na produtividade e o Entity Framework é um dos melhores frameworks neste quesito. Com o uso do mesmo você poderá aproveitar as facilidades do mapeamento objeto-relacional em sua aplicação, obtendo o máximo de produtividade na persistência e recuperação de dados.

Na última edição vimos os principais conceitos sobre frameworks ORM, onde apresentamos alguns dos principais benefícios do uso dos mesmos. Hoje conheceremos um dos frameworks mais populares no mundo .NET, o Entity Framework.

O Entity Framework nasceu no service pack do .NET framework 3.5, no Visual Studio 2008, e de lá para cá vem ganhando várias atualizações e se tornando uma ótima escolha como framework ORM para diversos tipos de projetos. É digno de nota que o Entity Framework, a partir de agora chamado apenas de EF, não possui uma versão 2 ou 3. O motivo é que quando um novo release ia ser lançado o .NET 4 estava para ser lançado também e por isso o EF adotou o número de versão do .NET, mas quando foi lançada a versão 5 do EF, o .NET lançava sua versão 4.5, desde então o número da versão do EF deixou de acompanhar o número da versão do .NET framework.

Uma característica fundamental do EF é que o mesmo possui três metodologias para o desenvolvimento da sua camada de acesso a dados: Database First, Model First e a Code First. As duas primeiras são centradas no trabalho visual, construindo um modelo que é gerado em um arquivo com extensão .edmx, já a terceira forma possui a centralização em classes POCO (Plain Old CLR Object). Veremos mais sobre os três modelos no decorrer do artigo.

Trabalhando com Entity Framework no seu projeto

O EF em sua versão 4 veio incorporado ao .NET framework 4, mas suas versões superiores são conseguidas usando o Nuget (Nota 1).

Nota: O Nuget é uma extensão do Visual Studio que facilita a instalação e o controle de bibliotecas e ferramentas. Existem duas formas de utilizá-lo: uma visual e uma textual (console) na qual se digita os comandos para instalação e atualização das bibliotecas. O Nuget realiza os downloads necessários para você, sem que seja necessário buscar bibliotecas em várias fontes diferentes.

Por default, se você não adicionar a versão 5 ou posterior, ao iniciar um projeto no .NET 4 ele já irá usar o EF 4. Para adicionar a versão 5 pode-se acessar o Package-Manager Console e executar o seguinte comando: “Install-Package EntityFramework e ele instalará a última versão automaticamente.

Outra maneira de adicionar o EF no projeto é de forma visual, selecionando o projeto na Solution Explorer, clicando com o botão direito e escolhendo a opção Manager NuGet Packages, onde será aberta a janela demonstrada na Figura 1. Nessa janela você pode pesquisar por “Entity Framework” na opção online. O NuGet irá baixar e adicionar a DLL ao seu projeto.

Adicionando o Entity Framework em sua última versão
Figura 1. Adicionando o Entity Framework em sua última versão

Abordagem database first

Nem sempre que começamos um projeto temos controle do banco de dados do mesmo. Algumas vezes o banco foi projetado por outra pessoa ou mantido por seu DBA. Em outros casos, você também pode ter mais facilidade em lidar primeiro com banco de dados, o que é bastante comum de acontecer principalmente em equipes mais acostumadas com o modelo relacional do que com o modelo orientado a objetos. Nesses casos você pode usar o EF normalmente, optando pela forma Database First.

O Database First foi a primeira modalidade lançada com o EF, ele criará um modelo de dados chamado EDM (Entity Data Model) em uma representação visual (extensão .edmx) formado por códigos XML.

A ideia é que ele leia o seu banco de dados e faça uma engenharia reversa, carregando quais serão as classes que vão representar as tabelas do banco.

Conhecendo o Contexto do Entity Framework

O contexto é como uma caixa onde estarão nossas entidades. Podemos trabalhar com nossas entidades normalmente e depois adicionar as mesmas ao contexto. Todas as mudanças feitas no contexto não serão persistidas no banco de dados até que seja invocado o método SaveChanges() do mesmo.

Explicando com mais detalhes, o contexto controla o ciclo de vida das mudanças feitas nas entidades. Se alterarmos alguma entidade fora do contexto, as mudanças não poderão ser persistidas, além disso, cada contexto é isolado um do outro. A ideia é que o contexto vigie as entidades e perceba as mudanças ocorridas com elas.

O DbContext é uma abstração para trabalhar com o ObjectContext, responsável pelo contexto dos dados, incluindo o estado das entidades e suas conexões. Ele utiliza dois patterns comuns em ORMs, o primeiro é o Unit of Work que trabalha com a ideia de guardar numa espécie de sessão o estado do objeto e vigiar suas mudanças em memória. É por isso que há o método SaveChanges, pois todo o estado das entidades fica mantido somente em memória, até que esse método seja invocado e as mudanças sejam persistidas na base de dados.

O padrão Repository é o segundo, cuidando da abstração do domínio e encapsulando sua persistência, agindo como uma coleção de objetos. Quando usamos um método para, por exemplo, adicionar um item a uma lista de determinada entidade (pelos métodos AddToNomeDaEntidade ou NomeDaEntidade.Add), estamos adicionando o item ao repositório desta entidade.

No Repository criamos uma interface que irá ser chamada por toda nossa camada de acesso a dados e irá trabalhar com o tipo passado, cuidando de sua persistência.

Além disso, dentro do contexto do EF as entidades possuem um estado, sendo que quando criamos uma nova entidade ela possui um estado chamado Detached, esse estado é guardado pelo contexto seguindo o padrão Unit of Work, porém, quando adicionamos a entidade ao contexto ela passa a ter um estado chamado Added. Por outro lado, se uma entidade foi selecionada no banco (portanto ela existe), mas seu estado não foi modificado, este se chama Unchanged, porém quando realizarmos alguma alteração na mesma ela passará a ser tratada como Modified. Caso a mesma seja excluída seu estado será Deleted.

Esses estados são importantes, pois o método SaveChanges irá agir de forma diferente para cada um deles. Por exemplo: as entidades em estado Detached não sofrerão nenhuma alteração. Uma entidade com estado Added será criada na base de dados e passará a ser Detached. Uma entidade Modified será alterada na base de dados e depois também retornará como Detached, porém uma entidade Deleted será excluída e não retornará ao contexto. As entidades em estado Unchanged não serão alteradas.

Esse controle é fundamental para que o EF saiba quais comandos SQL serão usados na base de dados e quais entidades deixarão de fazer parte do contexto. No caso de entidades Unchanged, teremos economia de processamento no banco, pois essas entidades não serão atualizadas.

Mapeando as Entidades no EDMX

Veremos então como é simples trabalhar na forma Database First. Vamos fazer um pequeno exemplo usando a base de dados AdventureWorksDB, que trata-se de uma base de dados de exemplo que pode ser encontrada na seção de Links deste artigo.

Inicie um novo projeto do tipo Console Application no Visual Studio. Neste exemplo o projeto foi nomeado como Adventure-Database-First em uma solution chamada Adventure-First.

A primeira coisa que vamos fazer no projeto é adicionar o nosso EDMX. Para isso clique com o botão direito no projeto e selecione Add > New Item, na janela que aparecer selecione a categoria Data e dentro desta escolha o item ADO.NET Entity Data Model, alterando o nome para AwModel.

Na janela seguinte teremos duas opções: Generate from Database e Empty Model. Iremos escolher a primeira opção, onde selecionaremos uma base já existente, a do AdventureWorks.

Para gerarmos o modelo vamos precisar escolher uma base já existente, por isso temos que criar uma conexão com o SQL Server na próxima janela. Para criar uma nova conexão clique no botão New Connection e insira os dados da conexão. Neste caso a conexão foi configurada para acessar diretamente o banco Adventure Works.

Na Figura 2 podemos ver a tela onde configuramos a connection string. A connection string de um EDMX é um pouco diferente de uma connection string comum, pois além de dados da conexão existem também dados para o mapeamento.

Configuração da connection string
Figura 2. Configuração da connection string

Na próxima tela, representada na Figura 3, podemos escolher os objetos do banco de dados que serão mapeados para a nossa aplicação. No nosso EDMX nós podemos mapear tabelas, views e procedures. Caso a base de dados sofra uma alteração no futuro, também poderemos atualizar o modelo, sem precisar criar um novo.

Tela de escolha de database objects
Figura 3. Tela de escolha de database objects

Uma opção interessante nessa tela é a pluralização dos nomes dos objetos gerados. Essa opção é muito útil, porém pode causar algum desconforto se a base de dados estiver com os nomes das tabelas em português. A pluralização consiste em pegar o nome de uma tabela e representá-lo em plural para a lista de objetos no contexto do EF, sendo o nome no singular utilizado para representar uma classe somente. Sendo assim, essa opção não só pluraliza como singulariza, caso o nome de uma tabela esteja no plural.

Dessa maneira, ao criar uma instância de Adress, por exemplo, você possui um só Adress, mas ao colocá-lo no contexto do EF, você tem uma “lista” de Adress, chamada de Addresses. É um recurso interessante para não confundir os desenvolvedores, porém as regras de gramática são as regras da língua inglesa. Por isso, se os nomes das suas tabelas estiverem em português, você vai se deparar com casos estranhos, como por exemplo, a tabela Carro que acaba se tornando Carroes.

A outra opção é a de inclusão de foreign keys no seu modelo, sendo esta uma opção sempre válida, pois com ela você pode navegar entre as classes de seu modelo mantendo as ligações por chaves existentes entre as tabelas que elas representam.

Para o nosso exemplo você não precisa selecionar todas as tabelas. Vamos marcar apenas as tabelas que possuem a palavra “Person” entre parênteses. Isso indica que fazem parte do mesmo database owner. A Figura 4 mostra o modelo gerado.

Modelo gerado com base nas tabelas escolhidas
Figura 4. Modelo gerado com base nas tabelas escolhidas

Aqui as tabelas foram organizadas na tela para mostrá-las juntas, mas no modelo que você verá as entidades estarão visualmente mais separadas. Cada uma dessas entidades representa uma tabela, onde as propriedades são colunas e as Navigations Properties são as chaves estrangeiras. Além disso, também é possível visualizar os relacionamentos entre as entidades.

Além dessa representação visual, temos as partial classes representando as entidades. O arquivo EDMX é um arquivo somente visual, baseado em XML. Para ver mais sobre as entidades basta acessar o arquivo Model.desing.cs.

Alterações em seu EDM

Se esquecermos de alguma tabela ou acrescentarmos uma nova à nossa base de dados nós podemos clicar com o botão direito em cima do modelo no EDM e selecionar a opção “Update from Database...”, essa opção abrirá a janela de escolha de objetos da base de dados, como a que usamos anteriormente, só que um pouco modificada.

Na primeira aba teremos os objetos que desejamos adicionar no modelo, como tabelas que não tinham sido adicionadas ou tabelas novas. Na segunda temos a opção de alterar o modelo atualizando-o, caso alguma tabela tenha tido sua estrutura modificada na base de dados. A terceira aba é aconselhável para os casos em que tivermos mudança em algum campo ou tabela e desejamos atualizar o modelo para ficar de acordo com as mudanças.

Outra coisa que podemos fazer é alterar características das entidades ou das propriedades no modelo. Para isso, podemos acessar o EDM Designer, selecionar uma entidade e no painel de Properties podemos alterar os dados que desejarmos.

Além de tabelas, também podemos mapear procedures de nossa base de dados. Na janela de seleção de objetos SQL podemos abrir a chave de Stored Procedures e escolher as procedures que desejarmos. Depois de escolher as procedures precisamos conectá-las a uma entidade para que elas possam ser utilizadas como métodos.

Pra realizar este procedimento basta selecionar uma entidade relacionada com a procedure desejada, clicar com o botão direito e escolher a opção Stored Procedure Mapping que uma aba será exibida na parte inferior do Visual Studio. Nela veremos opções para escolha de quais procedures serão utilizadas, como demonstra a Figura 5, onde podemos escolher quais itens serão os parâmetros e qual o retorno esperado pela procedure.

Aba para configurar mapeamento de procedure
Figura 5. Aba para configurar mapeamento de procedure

Um CRUD com Database First

Quando vamos salvar um novo item precisamos apenas iniciar uma instância do tipo desejado, incluir seus dados, adicionar ao contexto e salvar o contexto. Vejamos o código descrito na Listagem 1 onde primeiramente foi criada uma instância do contexto (linha 5), depois uma instância de Address (linha 7), que é uma classe que representa a tabela de mesmo nome no banco de dados. Depois de preenchidos os dados, o objeto foi adicionado ao contexto com o método AddToAddresses (linha 16), que irá adicionar este novo endereço à listagem de endereços já existente. Para persistir as alterações temos o método SaveChanges (linha 17).

01     class Program
02         {
03             static void Main(string[] args)
04             {
05                 AdventureWorks2008Entities context = 
                    new AdventureWorks2008Entities();
06     
07                 Address address = new Address();
08                 address.AddressLine1 = "Rua dos Bobos, 0";
09                 address.AddressLine2 = "Bairro do Morais";
10                 address.City = "Vinicius";
11                 address.PostalCode = "00000-000";
12                 address.StateProvinceID = 1;
13                 address.rowguid = new Guid();
14                 address.ModifiedDate = DateTime.Now;
15     
16                 context.AddToAddresses(address);
17                 context.SaveChanges();
18             }
19         }
Listagem 1. Salvando um endereço novo

Quando temos uma instância que foi resgatada do contexto, ao alterar a mesma o contexto irá salvá-la. Caso seja necessário buscar uma entidade da base de dados, podemos proceder de duas maneiras: Utilizando LINQ to Entities ou Lambda Expressions.

Na Listagem 2 veremos como selecionar o endereço que salvamos utilizando LINQ to Entities.

  01     class Program
  02         {
  03             static void Main(string[] args)
  04             {
  05                 AdventureWorks2008Entities context = 
                     new AdventureWorks2008Entities();
  06               Address address = (from a in context.Addresses 
                   where a.AddressID == 32525 select a).First();
  07                 address.City = "Rio de Janeiro";
  08                 context.SaveChanges();
  09             }
  10         }
Listagem 2. Selecionando um endereço usando LINQ

O uso do LINQ é bem fácil, é um jeito que pode ser considerado até mais lógico do que usar linguagem SQL. Nesse caso, primeiro indicamos de onde seria retirado o address, que é da lista Addresses dentro do contexto. Depois indicamos a condição de restrição, onde passamos um id fixo e por fim temos o select. Dessa expressão em LINQ foi chamado o método First, que vai pegar o primeiro valor retornado e passar para o tipo Address requisitado.

Em seguida, alteramos o nome da cidade (linha 7) e as mudanças do contexto foram salvas. O contexto percebeu que o status de uma entidade foi alterado e persistiu as informações na base de dados.

O mesmo comando para selecionar um objeto do contexto poderia ser feito com uma expressão Lambda, como é mostrado na Listagem 3.

01     class Program
02         {
03             static void Main(string[] args)
04             {
05                 AdventureWorks2008Entities context = 
                   new AdventureWorks2008Entities();
06                 Address address = context.Addresses.Single
                  (a => a.AddressID == 32525);
07            }
08         }
Listagem 3. Selecionando um endereço usando lambda

A principal diferença entre o linq e as expressões lambda fica principalmente por conta da sintaxe, cabendo ao desenvolvedor escolher a maneira com que o mesmo melhor se adapta. Em alguns casos um pouco mais complexos, com mais condições e agrupamentos, pode ser mais simples trabalhar com linq, visto que a maioria dos desenvolvedores considera que o mesmo possui uma linguagem mais amigável. Com isso já vimos como selecionar e alterar uma entidade, para excluir uma entidade temos o método DeleteObject, que pode ser visto na Listagem 4.

  01     class Program
  02         {
  03             static void Main(string[] args)
  04             {
  05                 AdventureWorks2008Entities context = 
                     new AdventureWorks2008Entities();
  06                 Address address = context.Addresses.Single
                     (a => a.AddressID == 32525);
  07                 context.Addresses.DeleteObject(address);
  08
  09               context.SaveChanges();
  10        } 
  11      }
Listagem 4. Deletando um objeto

Nesta listagem nós selecionamos um objeto utilizando Lambda (linha 6) e em seguida usamos o método DeleteObject (linha 7), passando como argumento o objeto que desejamos excluir. Lembrando que para persistir as mudanças é preciso invocar o método SaveChanges (linha 9).

Abordagem Model First

O Model First é um caminho também centrado no EDM, porém com a diferença de podermos modelar nosso domínio e gerar nossa base a partir dele.

A criação do modelo começa adicionando um EDM ao seu projeto, só que desta vez é preciso adicionar um modelo vazio, escolhendo a opção “Empty Model”. Além disso, vamos nos deparar com a tela do EDM, só que vazia, bastando então que façamos a modelagem utilizando o EDM designer. Para adicionar entidades ao modelo podemos usar a toolbox e arrastar itens para a região central da tela.

A toolbox ilustrada na Figura 6 mostra os cinco itens que temos disponíveis para a nossa modelagem. Como normalmente é encontrado no fim das categorias da toolbox, podemos ver um grupo chamado General, que pode conter itens de terceiros.

O Pointer não é um item que arrastamos para o EDM, mas sim a indicação de que estamos no modo de selecionar itens existentes no EDM. Quando selecionamos um item existente no EDM, podemos alterar as propriedades deste utilizando a aba Properties.

O item Association serve para relacionar entidades. Podemos alterar o tipo de relacionamento como 1 para 1, 1 para muitos ou muitos para muitos.

O terceiro item da toolbox é o principal item, o Entity, o qual usamos para adicionar as entidades no nosso modelo, que provavelmente se tornarão tabelas no banco de dados.

O quarto item é o menos usado, o Inheritance, que serve para relacionar entidades por meio de herança. Será um item tratado no decorrer do artigo.

A toolbox para modelagem no seu EDM
Figura 6. A toolbox para modelagem no seu EDM

Ao adicionar uma entidade no modelo, clicando em Entity na sua toolbox e arrastando para seu modelo, ela vai estar com a aparência padrão, sem propriedades e sem nome. Clicando no nome default dela, podemos editar o nome direto no modelo. Outra forma de alterar o nome da mesma é selecionando a entidade e trocando o nome da mesma na aba Properties, como podemos ver na Figura 7.

A aba de propriedades da entidade
igura 7. A aba de propriedades da entidade

Na aba de propriedades da entidade temos propriedades ligadas à geração de código, como se a entidade será abstrata e qual a forma de acesso a ela. Em propriedades gerais temos o tipo base (caso seja usada herança), a parte de documentação onde podemos dar uma descrição mais detalhada para a entidade e uma descrição resumida. Também temos o nome da entidade e o Entity Set Name, que se refere ao nome da lista de entidades (como o Addresses que representava uma lista de objetos Address). Você verá esse nome ao adicionar uma nova instância da entidade ao contexto.

A primeira propriedade que já vem adicionada à entidade é o Id, mas podemos adicionar mais propriedades, para isso, primeiro devemos selecionar a entidade na parte de propriedades, dar um clique com o botão direito e selecionar a opção Add. Há três opções de propriedades: Scalar, Navigation e Complex.

Uma propriedade escalar é uma propriedade que ocupa uma coluna na sua tabela, como um nome ou uma data. Já uma propriedade de navegação será transformada em foreign key no banco de dados. Por fim, uma propriedade complexa se refere a outras entidades que não possuem chaves, usadas para dados complementares. São como um agrupamento de propriedades escalares que formam um conjunto de informações sobre outra propriedade.

Criando uma associação entre as entidadades

Quando temos duas entidades que se relacionam, podemos arrastar o item Association para o EDM Desingner e selecionar as entidades que desejamos realizar o relacionamento. Por padrão, será assumido o relacionamento de um para muitos, como demonstra a Figura 8.

Uma relação entre duas entidades
Figura 8. Uma relação entre duas entidades

Outra forma de adicionarmos um relacionamento é selecionando uma entidade, clicando com o botão direito e selecionando a opção add association. A diferença é que será aberta uma janela para configurar a associação, como mostrado na Figura 9.

Configuração de uma associação
Figura 9. Configuração de uma associação

Nesta tela podemos dar um nome para a associação, selecionar a multiplicidade e configurar para que seja gerada uma foreign key na entidade selecionada. Essas propriedades poderão ser alteradas quando selecionarmos a associação no EDM Designer e acessarmos a aba propriedades.

Gerando o script de criação de base de dados

Depois de ter modelado nossas entidades, podemos gerar um script para a criação da base de dados. Para isso, basta clicarmos com o botão direito em alguma parte da área do EDM Designer e selecionar a opção “Generate Database from Model”. Na última janela do wizard poderemos ver os comandos SQL gerados e clicar em finalizar, porém a base de dados não vai ter sido criada ainda.

Em uma nova janela veremos o código gerado, como na Listagem 5.

  01     SET QUOTED_IDENTIFIER OFF;
  02     GO
  03     USE [Exemplo];
  04     GO
  05     IF SCHEMA_ID(N'dbo') IS NULL EXECUTE(N'CREATE SCHEMA [dbo]');
  06     GO
  07     CREATE TABLE [dbo].[Consoles] (
  08         [Id] int IDENTITY(1,1) NOT NULL,
  09         [Name] nvarchar(max)  NOT NULL
  10     );
  11     GO
  12     CREATE TABLE [dbo].[Games] (
  13         [Id] int IDENTITY(1,1) NOT NULL,
  14         [Title] nvarchar(max)  NOT NULL
  15     );
  16     GO
  17     CREATE TABLE [dbo].[GameConsole] (
  18         [Game_Id] int  NOT NULL,
  19         [Consoles_Id] int  NOT NULL
  20     );
  21     GO
  22     ALTER TABLE [dbo].[Consoles]
  23     ADD CONSTRAINT [PK_Consoles]
  24         PRIMARY KEY CLUSTERED ([Id] ASC);
  25     GO
  26     ALTER TABLE [dbo].[Games]
  27     ADD CONSTRAINT [PK_Games]
  28         PRIMARY KEY CLUSTERED ([Id] ASC);
  29     GO
  30     ALTER TABLE [dbo].[GameConsole]
  31     ADD CONSTRAINT [PK_GameConsole]
  32         PRIMARY KEY NONCLUSTERED ([Game_Id], [Consoles_Id] ASC);
  33     GO
  34     ALTER TABLE [dbo].[GameConsole]
  35     ADD CONSTRAINT [FK_GameConsole_Game]
  36         FOREIGN KEY ([Game_Id])
  37         REFERENCES [dbo].[Games]
  38             ([Id])
  39         ON DELETE NO ACTION ON UPDATE NO ACTION;
  40     GO
  41     ALTER TABLE [dbo].[GameConsole]
  42     ADD CONSTRAINT [FK_GameConsole_Console]
  43         FOREIGN KEY ([Consoles_Id])
  44         REFERENCES [dbo].[Consoles]
  45             ([Id])
  46         ON DELETE NO ACTION ON UPDATE NO ACTION;
  47     CREATE INDEX [IX_FK_GameConsole_Console]
  48     ON [dbo].[GameConsole]
  49         ([Consoles_Id]);
  50     GO
Listagem 5. Código sql gerado pelo Model First

Como nossa connection string estava configurada para o SQL Server, foi gerado um script de acordo com a sintaxe do mesmo. Como podemos ver o código gerado é bem organizado, com isso podemos pegar o mesmo e validá-lo junto ao DBA ou executá-lo diretamente no Visual Studio.

É importante destacar que só serão criadas as tabelas, sendo que a base de dados precisa já estar criada para execução do SQL.

Abordagem Code First

O Code First é uma forma diferente de trabalhar, na qual você descreve suas classes de entidade antes de criar a base de dados, deixando assim que o EF crie a base de dados para você. Quando criamos nossas classes de entidade, criamos classes POCO (Plan Old CLR Objects), que são classes que utilizam somente tipos clássicos do CLR, ou seja, somente tipos do .NET Framework, sem intervenção de tipos de outros frameworks ou bibliotecas, sem depender nem mesmo do EF. O bom é que assim você cria classes de entidades que podem ser plugáveis a outros projetos, mesmo que estes não utilizem o EF para a persistência de dados.

Vamos então fazer um exemplo bem simples para visualizarmos como começar a trabalhar com EF Code First. Primeiramente abra seu Visual Studio e crie um projeto novo do tipo Console Application. Aqui foi dado o nome de “Relacionamentos”, porque será o mesmo projeto que será usado para explicar relacionamentos entre tabelas no EF Code First mais à frente no artigo.

O segundo passo a fazer é adicionar a última versão do EF, como explicado no inicio do artigo. Agora vamos criar uma classe POCO com o nome de “Revista”, na própria raiz do projeto. Na Listagem 6 vemos o código da classe com algumas propriedades.

  01     using System;
  02     using System.Collections.Generic;
  03     using System.Linq;
  04     using System.Text;
  05     namespace Relacionamentos
  06     {
  07         public class Revista
  08         {
  09             public int Id { get; set; }
  10             public string Nome { get; set; }
  11         }
  12     }
Listagem 6. Classe Revista

Como se pode perceber, na lista de usings não foi referenciado o Entity Framework, pois essa é uma classe POCO, independente de frameworks e bibliotecas. Temos somente duas propriedades, Id e Nome, só para ilustrar o exemplo.

Precisamos agora criar um DbContext, que será quem vai controlar quais são as nossas classes que fazem parte do contexto de dados. Crie então outra classe chamada RevistaContext, esta irá precisar referenciar o namespace do Entity Framework, como mostra na Listagem 7.

  01     using System;
  02     using System.Collections.Generic;
  03     using System.Linq;
  04     using System.Text;
  05     using System.Data.Entity;
  06     namespace Relacionamentos
  07     {
  08         public class RevistaContext:DbContext
  09         {
  10             public DbSet<Revista> Revistas { get; set; }
  11         }
  12     }
Listagem 7. Classe RevistaContext

Como podemos ver na Listagem 7, essa classe extende da DbContext e possui somente uma única propriedade: Revistas. Essa propriedade será o repositório de Revista, como aquela lista de objetos em que adicionavamos itens no modelo DataBase First, por isso usamos nomes pluralizados.

Até agora o EF não criou nossa base de dados. Caso possua o SQL Server Manager Studio, abra e veja que em seu banco local que não possui nada relacionado a revistas. Caso você não possua esse aplicativo, provavelmente terá um SQL Server Express, porém somente acessivel via shell ou pelo Visual Studio, pois ao intalar esse IDE, ele instala uma instância express do SQL Server na máquina para testes.

Mas para o EF criar a sua base de dados, você vai precisar executá-lo no seu código. Na primeira chamada para o banco ele vai ver se a base já existe e, em caso negativo, irá criar a mesma.

Vamos então adicionar uma revista chamada “easy .NET Magazine”, sem precisar adicionar um número de Id e mandar salvar. Para isso vamos colocar nosso código diretamente no método main da classe Program, como demonstrado na Listagem 8.

  01     class Program
  02         {
  03             static void Main(string[] args)
  04             {
  05                 RevistaContext db = new RevistaContext();
  06                 Revista revista = new Revista();
  07                 revista.Nome = "easy .NET Magazine";
  08                 db.Revistas.Add(revista);
  09                db.SaveChanges();
  10             }
  11         }
Listagem 8. Salvando um item novo no EF Code First

Como podemos ver na Listagem 8, é muito parecido com o modo como adicionamos uma entidade nova no DataBase First, com a diferença do método de adicionar, que acessamos a partir do repositório de Revistas de nosso contexto.

Quando você abrir o seu SQL Server Manager Studio, verá que foi criado um schema, diferente do que seria no Model First, com o nome do projeto acrescido de ponto e o nome da sua classe de DbContext, como podemos ver na Figura 10.

Schema e tabelas criadas pelo EF Code First
Figura 10. Schema e tabelas criadas pelo EF Code First

De acordo com a figura, foi criada uma tabela Revistas e mais uma tabela em System Tables chamada MigrationHistory. Essa tabela foi criada para o controle de versão da base de dados, um recurso bastante interessante do EF chamado Migrations, mas este recurso é assunto para outro artigo.

Se observarmos, a única linha criada na tabela Revistas possui o nome da revista que tínhamos cadastrado e o campo Id já está preenchido com o número 1. Acontece que o EF, ao criar a tabela, já cria o campo Id para ser Identity, isso significa que ela é auto incrementável.

Estratégias para criação de base de dados

Quando executamos nossa Console Aplication, foi criada a base de dados automaticamente. Há três estratégias para criação da base de dados.

  • CreateDatabaseIfNotExists - Essa estratégia faz com que o EF identifique se não existe uma base de dados e então crie uma nova. Essa é a estratégia default.
  • DropCreateDatabaseWhenModelChanges - Essa é um pouco diferente, pois só cria uma nova base de dados se houverem mudanças, e isso significa que a base antiga vai ser deletada, então você vai perder todos os dados.
  • DropCreateDatabaseAlways - Nessa forma sua base vai ser sempre apagada e criada novamente.

Para alterar isso devemos criar uma classe de inicialização de nosso contexto, como descrito na Listagem 9. Nesse esquema vamos poder criar essa classe e adicionar no método Seed, que serve para iniciar o banco de dados, adicionando os dados importantes que devem estar presentes quando a aplicação começar a rodar.

  01     public class RevistaContextSeedInitializer : DropCreateDatabaseAlways
        <RevistaContext>
  02         {
  03             protected override void Seed(RevistaContext context)
  04             {
  05     
  06             }
  07         }
Listagem 9. Criando o inicializador o banco do nosso projeto

Quando você utiliza a opção de inicialização CreateDatabaseIfNotExists e altera ou adiciona uma propriedade, ou até mesmo uma nova entidade, o EF irá acusar erro ao executar, pois ele somente irá criar uma base de dados se ela não existir, e você propôs uma alteração.

Neste caso você irá precisar lidar com Migrations ou com uma das outras duas opções de inicialização de base de dados.

Alterando as propriedades de campos no Code First

Se observarmos a tabela criada pelo EF, veremos que o campo Nome está como nvarchar(MAX), porém não somos obrigados e ter todos os nossos campos strings criados desta forma. Por isso, existem duas formas de realizarmos o mapeamento de nossos campos: Data Annotations e Fluent API.

Data Annotations são anotações que adicionamos aos códigos antes das propriedades das nossas classes de modelo, colocando os mesmos entre chaves. Um exemplo é demonstrado na Listagem 10.

  01     public class Revista
  02         {
  03             [Key]
  04             public int Id { get; set; }
  05             [StringLength(100)]
  06             public string Nome { get; set; }
  07         }
Listagem 10. Usando Data Annotations para configurar as propriedades

Na Listagem 10 vemos duas data annotations: Key, que indica que a propriedade Id é uma primary key (que não é necessária, pois como a propriedade se chama Id, o EF entende que seja uma primary key) e depois a StringLength, que indica o número máximo de caracteres para um campo do tipo string. Com este mapeamento, ao acessarmos o banco de dados, veremos que o campo agora é um nvarchar(100).

As Data Annotations também são úteis se você estiver fazendo uma aplicação Asp.NET MVC, pois o que você colocar de regra com uma annotation vai virar um validador nas suas views.

Para saber mais sobre as Data Annotations visite o link da seção Links desse artigo.

Fluent API é outra forma de configurar o mapeamento de nossas propriedades, porém neste caso precisamos colocar as configurações dentro da sua classe de contexto. Essa forma é procurada por muitos, pois deixa as classes de modelo limpas, continuando a serem POCO (pois não dependem mais do EF).

Um aspecto que alguns consideram como negativo é que a Fluent API é menos simples, para usá-la precisamos de lambda expressions, como no exemplo da Listagem 11.

  01     public class RevistaContext : DbContext
  02         {
  03             public DbSet<Revista> Revistas { get; set; }
  04     
  05             protected virtual void OnModelCreating
                 (DbModelBuilder modelBuilder)
  06             {
  07                 modelBuilder.Entity<Revista>()
                     .Property(r => r.Nome).IsRequired();
  08             }
  09         }
Listagem 11. Usando Fluent API para configurar as propriedades

Neste exemplo foi indicado que a propriedade Nome da entidade Revista é obrigatória. Para visualizar todas as propriedades que você pode configurar, podemos colocar o nome da propriedade e esperar o IntelliSense completar a mesma. Alguns exemplos são HasMaxLength, que define o tamanho máximo em campos string e HasColumnName, que cria a coluna na base de dados com um nome diferente do nome da propriedade da classe.

Relacionamentos entre entidades

Analisando nosso exemplo, a classe revista pode ter várias edições, mas uma edição pertence somente a uma revista. Essa é uma forma de relacionamento entre entidades, porém temos três formas de relacionamentos, descritas a seguir.

Relacionamento Um para um

Um para um, em inglês One to One, é um relacionamento onde uma entidade só possui uma ligação com outra entidade e esta só possui a ligação de volta. Como por exemplo, o logo de da revista. A revista possui um único logo que por sua vez está vinculado a apenas uma revista.

Na base de dados isso é representado por foreign keys nas duas tabelas, ligando uma ao id da outra. Vamos ver como colocamos isso no código com duas classes POCO na Listagem 12.

Nota: Quando adicionarmos um modelo novo que deseje que seja mapeado na base de dados, é preciso adicionar ele à nossa classe de Contexto usando a propriedade DbSet.
  01     public class Logo
  02         {
  03             public int Id { get; set; }
  04             public string Descricao { get; set; }
  05         }
  06     public class Revista
  07         {
  08             public int Id { get; set; }
  09             public string Nome { get; set; }
  10             public Logo logo { get; set; }
  11         }
  12     class Program
  13         {
  14             static void Main(string[] args)
  15             {
  16                 Database.SetInitializer<RevistaContext>
                     (new RevistaContextSeedInitializer());
  17                 RevistaContext db = new RevistaContext();
  18                 Revista revista = new Revista();
  19                 revista.Nome = ".NET Easy Magazine";
  20                 Logo logo = new Logo();
  21                 logo.Descricao = "Azul";
  22                 db.Logos.Add(logo);
  23                 revista.logo = logo;
  24                 db.Revistas.Add(revista);
  25                 db.SaveChanges();
  26             }
  27         }
Listagem 12. Relacionamento Um para Um

Na Listagem 12 há a nova classe de Logo e a classe Revista com uma nova propriedade, que é o logo. Depois adicionamos um logo novo e ligamos o mesmo a uma revista.

Na base de dados, vemos que na tabela Revista foi adicionado um campo do tipo inteiro com o nome de logo_id e a tabela possui uma associação, porém o campo não é uma foreign key e não é obrigatório. Podemos determinar isso com Fluent API, indicando que o campo que liga o logo a uma revista é obrigatório, indo no método OnModelCreating e adicionando o código abaixo:

modelBuilder.Entity().HasRequired(r => r.logo);

Relacionamento Um para muitos

No casso de uma Revista ter muitas Edições teremos um relacionamento um para muitos, ou em inglês One to Many. Vamos então criar uma classe de modelo com o nome de Edicao e colocar na classe Revista que ela possui uma lista de Edições, como descrito na Listagem 13.

  01     public class Edicao
  02     {
  03            public int Id { get; set; }
  04            public string Descricao { get; set; }
  05     }
  06     public class Revista
  07     {
  08            public int Id { get; set; }
  09           public string Nome { get; set; }
  10           public Logo logo { get; set; }
  11           public List<Edicao> Edicoes { get; set; }
  12      }
  13     //Adicionando o relacionamento no OnModelCreating
  14            modelBuilder.Entity<Revista>().HasMany(r => r.Edicoes);
  15     //Adicionando uma edição
  16                 Edicao edicao = new Edicao();
  17                 edicao.Descricao = "edição 28 - entendendo ORM";
  18                 revista.Edicoes.Add(edicao);
Listagem 13. Um para muitos

Muitos para muitos

O relacionamento muitos para muitos não difere muito, só que será criada na base de dados uma tabela complementar, somente para ligar os Ids das duas entidades. No exemplo, digamos que temos autores que escrevem para mais de uma revista, um autor pode escrever para várias revistas, assim como uma revista pode ter vários autores.

A diferença entre esse exemplo e o de um para muitos só muda porque teremos uma lista nas duas entidades e a forma que mapearemos no Fluent API, como mostrado no código abaixo:

modelBuilder.Entity().HasMany(r => r.Autores).WithMany(a => a.Revistas);

Mapeando uma base já existente para Code First

No Code First você cria as classes de entidade antes de criar a base de dados, porém muita gente gostaria de ter classes POCO com uma base já existente. Uma ferramenta oficial que pode ser utilizada é o Power Tools, que atualmente está em sua versão beta 3.

Você primeiro precisa fazer o download do plugin, da mesma forma que baixou o NuGet. Depois de ter instalado o Power Tools, você pode criar um projeto novo. Nesse exemplo foi adicionado um projeto chamado Adventure-First-Power-Tools. Depois dê um clique com o botão direito no nome do seu projeto, você verá que haverá uma opção a mais, chamada Entity Framework, como mostra a Figura 11.

Opção de engenharia reversa
Figura 11. Opção de engenharia reversa

A opção de Reverse Engineer Code First pede para que você configure uma conexão, depois ele vai fazer engenharia reversa e ler as suas tabelas, assim ele poderá montar suas classes POCO com modelos. Ele também criará classes de relacionamento utilizando Fluent API.

Depois de usar a engenharia reversa você poderá trabalhar com suas classes normalmente da mesma forma para o Code First.

Conclusão

O Entity Framework é uma ferramenta completa e complexa que possibilita muitas formas de se trabalhar na nossa camada de acesso a dados, uma ótima escolha como ORM. Além de permitir o mapeamento dos elementos para tornar as aplicações em OO, permite também que o desenvolvedor escreva as classes para então o framework crie sozinho as tabelas do banco, facilitando o trabalhando.


Saiu na DevMedia!
  • MVC e Regras de negócio:
    Regras de negócio são as diretrizes que a aplicação precisa seguir para funcionar como é esperado. Porém, quando utilizamos um padrão arquitetural, como o MVC, é comum que surja a dúvida de onde codificar essas regras, um assunto que abordamos no DevCast a seguir.
  • Curso de C#:
    C# é uma linguagem orientada a objeto com a qual podemos criar aplicações desktop, mobile e web. Para dar início ao seu aprendizado, trabalharemos em uma aplicação prática que será construída passo a passo, utilizando estruturas básicas do C# através do cenário de uma aplicação que pode calcular o signo do usuário de acordo com seu dia e mês de nascimento.

Saiba mais sobre .NET ;)

  • Guias de .NET:
    Aqui você encontra todos os Guias de estudo que você irá precisar para dominar por completo a linguagem .NET e todos seus recursos. Escolha o seu!
  • Linguagem C#:
    Neste guia de consulta você encontrará diversos conteúdos que podem ser usados ao longo dos seus estudos sobre a linguagem de programação C#. Consulte este guia para aprender mais sobre certos recursos da linguagem.
  • Cursos de .NET:
    Torne-se um programador .NET completo. Aprenda a desenvolver sites, web services e aplicações mobile utilizando a linguagem C# e os frameworks ASP.NET MVC, Web API, Entity Framework, Xamarin e mais.