ASP .NET - O acesso a dados usando o Entity Framework - II


O primeiro passo para iniciar com a utilização do Entity Framework em uma aplicação é gerar um Entity Data Model (EDM). O Entity Data Model (EDM) é o coração do Entity Framework. É nele que as informações do mapeamento entre as classes e o banco de dados reside.

Obs: Esta chegando agora ?  Então veja a primeira parte do artigo aqui : ASP .NET - O acesso a dados usando um ORM - Entity Framework - I

O EDM é composto por 3 arquivos XML:

  1. O primeiro descreve as classes do modelo de objetos;
  2. O segundo descreve as tabelas no banco de dados;
  3. O terceiro armazena o mapeamento entre os objetos e as tabelas;

A seguir vamos ver como tratar estes arquivos.

A criação e a manutenção manual do EDM pode ser um operação que consome muito tempo, por isso o Entity Framework possui um assistente e um descritor tanto no Visual Studio como nas versões Express que podemos utilizar para tratar os arquivos do EDM de forma visual sem nos preocuparmos com detalhes de sua estrutura.

Obs: Mais adiante veremos o recurso de utilizar classes POCO para definir o mapeamento e assim não ficarmos dependentes do EDM gerado pelo Entity Framework.

Recursos usados:

Criando um Entity Data Model

Vamos então criar um EDM usando como exemplo o banco de dados Northwind.mdf e tomando como base apenas as tabelas Orders, Order_Detail, Customer e Products.

Abra o Visual Web Developer 2010 Express Edition e crie um novo projeto do tipo Class Library. A seguir no menu Project clique em Add New Item e selecione o template ADO .NET Entity Data Model informando o nome Northwind.edmx e clicando no botão Add;

Para criar a EDM, temos que adicionar um novo item do tipo ADO.NET Entity Data Model ao projeto. Em uma aplicação com três camadas, o melhor lugar para colocar esse é em uma camada separada, como a camada de acesso a dados, mas isso vai depender da concepção que você usa para definir a arquitetura do seu projeto.

Se você adotar a concepção de projeto Domain Model, você provavelmente criar uma camada Model e vai criar um assembly separado que referencia um repositório.

Quando você clicar no botão Add o assistente vai entrar em ação.

O primeiro passo permite que você escolha se você quer criar um modelo a partir de um banco de dados ou a partir de um modelo vazio. A primeira opção vai criar as classes a partir do modelo com base nas tabelas do banco de dados. Na segunda opção você pode definir o modelo conceitual a partir do qual o mapeamento será gerado.

Como eu já tenho um banco de dados, estamos optando pela primeira escolha.

Escolhendo essa opção teremos no final do assistente as tabelas descritas no EDM, onde cada classe é gerada automaticamente para cada uma das tabelas.

As propriedades e as colunas também são mapeadas um-para-um.O assistente também inspeciona as chaves estrangeiras nas tabelas do banco de dados e as reflete nas classes.

Na próxima janela do assistente você deve escolher o banco de dados, no nosso exemplo Northwind, a partir da lista drop-down que já mostra as conexões existentes. Se não encontrar a conexão você deverá criá-la clicando no botão New Connection.

Após escolher o banco de dados você verá a string de conexão que o Entity Framework usa para se conectar com o banco de dados.

Na caixa de seleção - Save entity connnection settings in App.Config - permite escolher se você quer armazenar a conexão no arquivo de configuração.

Na última tela do assistente são apresentadas as tabelas, functions, views e stored procedures existentes no banco de dados.Aqui você seleciona as tabelas que deseja usar, no nosso exemplo usaremos as tabelas: Customers, Order Details, Orders e Products.

A seguir existe a opção para pluralizar ou singularizar os nomes das classes. Ao selecionar esta opção a tabela Orders, por exemplo, irá gerar uma classe Order e assim para as demais tabelas. Se você não marcar esta opção a tabela Orders gera uma classe Orders.

A outra opção permite escolher se você deseja criar uma propriedade chave estrangeira junto com a propriedade que referencia outro objeto como por exemplo além da propriedade Customer, você pode decidir incluir a propriedade CustomerId na classe Order. Usar propriedade de chave estrangeira torna o gerenciamento dos relacionamentos mais fácil.

Ao clicar no botão Finish o assistente cria as classes (não as tabelas) e as exibe no descritor. Você pode editar as classes para modificar os seus nomes ou os nomes de suas propriedades para adicionar relacionamentos com outras classes, para adicionar herança, e assim por diante. Você tem total controle sobre o seu modelo.

Na janela Solution Explorer no Visual Studio, você gera o arquivo com extensão .edmx gerado pelo assistente. Ele contém o três arquivos EDM fundidos num só, para que cada vez que você modifica uma entidade visualmente, a suas modificações sejam registradas dentro desse arquivo.

O arquivo EDMX não é o EDM.

O EDMX é um artefato para habilitar o designer. Não sendo compreendido pelo Entity Framework. Durante a compilação, o compilador divide o EDMX em três arquivos EDM e depois incorpora os arquivos para a montar ou salvar no diretório de saída de forma que o runtime do Entity Framework possa lê-los. O que o compilador faz depende do valor da propriedade de Processamento de Metadados do Artefato do designer. Os valores possíveis são Embed in a Output Assemblry e Copy to Output Directory.

Na janela Solution Explorer, a exibição Model Browser (fig 1.0) , exibe a estrutura do arquivo edmx com as entidades do modelo e a contrapartida das tabelas do banco de dados;

A figura 2.0 exibe as propriedades do modelo exibindo o nome do container que será o nosso contexto:

fig 1.0 - Estrura exibida no Model Brownse fig 2.0 - Nome do modelo

A último etapa a ser feita é habilitar o Entity Framework em sua aplicação copiando a seqüência de conexão gerada pelo assistente no arquivo de configuração de a aplicação. (A seqüência de conexão está no arquivo app.config). Dessa forma o Framework será configurado corretamente, e você pode começar a escrever código de acesso aos dados.

Se você ter uma espiada no código gerado pelo Entity Framework você vai ver que ele esta constituído com atributos para decorar as definições das classes, e possui embutido toda a estrutura do EF; em outras palavras o código gerado esta fortemente acoplado ao EF e isso não é muito recomendado visto que em uma concepção Domain Model as classes podem até ter conhecimento da camada de dados mas devem ser ignorantes quanto persistência, ou seja, no Domain Model cada classe deve ser um objeto POCO e se preocupar somente com o código de negócio.

Obs: A versão 4.1 do Entity Framework, diferentemente das versões anteriores, dá suporte as classes POCO de forma que podemos gerar o modelo de entidades usando apenas código nas classes POCO sem nenhum acoplamento com o Entity Framework.

Consultando o Modelo

Com o modelo de entidades gerado e o mapeamento ORM feito podemos usar o Entity Framework para consultar e persitir dados.

Realizar consultas em um banco de dados utilizando a abordagem clássica ADO.NET requer que você escreva código SQL e também crie várias classes para executar o código e para ler os dados retornados.

Realizar consultas usando o Entity Framework é diferente.

Você não trabalha diretamente com o banco de dados, pois a única estrutura que você precisa conhecer é o modelo de entidades. É assim que o modelo se torna o seu banco de dados e motivo das suas consultas serem escritas contra ele. O ônus de traduzir suas consultas em SQL e executar o código contra o banco de dados fica com o Entity Framework.

O Entity Framework é um grande passo para simplificar o desenvolvimento de aplicações com acesso a dados. Lembre-se que as classes no modelo podem ser diferentes das tabelas no banco de dados para as quais as classes são mapeadas. (As Classes possuem herança. O número de classes e tabelas também não têm que ser o mesmo, e assim por diante.)

Ao usar o Entity Framework você não precisa se preocupar com qualquer um desses problemas. Você pode escrever consultas contra o modelo, e depois o Entity Framework gera o SQL para você. Além do mais, as classes conseguem expressar melhor o negócio que as tabelas do banco de dados, então escrever consultas contra o modelo é uma abordagem mais orientada ao negócio.

Quando da geração do modelo de entidades o Entity Framework gerou um container que representa o modelo conceitual com o nome NorthwindEntities.

A classe NorthwindEntities representa o nosso contexto. A classe de contexto é a sua porta de entrada para o banco de dados. Essa classe herda de ObjectContext e tem um conjunto de entidades para cada classe que não herda de qualquer outro no modelo. No nosso caso, temos quatro classes e quatro conjuntos de entidades.

Se o contexto é a sua porta de entrada para o banco de dados, os entity sets (conjuntos de entidades) são porta de entrada para os dados. Pense neles como uma representação em memória de uma tabela. A classe de contexto expõe cada entidade definida como uma propriedade cujo nome é o nome da tabela (idênticos, no plural, ou singularizada, dependendo na sua escolha) e cujo tipo é ObjectSet<T>, onde T é a classe de modelo que é exposta (você pode modificar o nome da entidade definida usando o designer).

Para consultar o modelo, você tem que executar uma consulta LINQ to Entities contra um entity set. O LINQ to Entities é apenas um dialeto do LINQ que desencadeia o processo que transforma a consulta LINQ em SQL (em vez de realizar uma busca na memória). Agora que você tem os fundamentos, vamos começar a escrever a consultas para retornar informações do modelo criado pelo Entity Framework.

Nota: Nas consultas usadas para consultar o modelo eu vou mostrar tanto a sintaxe VB .NET como a C# dando ênfase na primeira para exibir o resultado.

Se você desejar reproduzir as consultas mostradas deverá seguir os seguintes passos:
  1. Incluir um novo projeto via menu File -> Add -> New Project usando o template ASP .NET Empty Web Application com o nome Web;
  2. Incluir uma referência ao projeto Modelo no projeto Web;
  3. Incluir uma referência ao namespace System..Data.Entity no projeto Web;
  4. Definir a string de conexão criada no arquivo app.Config no arquivo web.Config do projeto Web;

<connectionStrings>
<add name="NorthwindEntities" connectionString="metadata=res://*/Northwind.csdl|res://*/Northwind.ssdl|res://*/Northwind.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

  1. Incluir um Web Form no projeto Web com o nome Default.aspx;
  2. Definir os seguintes namespaces na página Default.aspx.vb :

    Imports Modelo
    Imports System.Data.entity

1- Consulta para obter os pedidos filtrando pelo nome da cidade de envio(cidade)

Using ctx = New NorthwindEntities()
     Return ctx.Orders.Where(Function(o) o.ShipCity = cidade)
End Using
using (var ctx = new NorthwindEntities())
{
   return ctx.Orders.Where(o => o.ShipCity == cidade);
}

VB .NET

C#

Observe que a primeira coisa a fazer é criar uma instância do contexto (NorthwindEntities) para poder usar os recursos do Entity Framework. Nas consultas seguintes eu não vou mais repetir esta linha de código mas ele sempre deve ser feita;

2- Consulta para obter os pedidos filtrando pelo nome do cliente (cliente)


  ctx.Orders.Where(Function(o) o.Customer.CompanyName = cliente)

    ctx.Orders.Where(o => o.Customer.CompanyName == cliente);

VB .NET

C#

Como vemos não precisamos especificar JOINS como faríamos se estivéssemos usando código SQL O Entity Framework le os relacionamentos no modelo (EDM) e automaticamente gera a junção correta para nós.

3- Consulta para calcular o valor total de um pedido e comparar com o valor do usuário.(valor)

ctx.Orders.Where(Function(o) o.Order_Details.Sum(Function(d) (d.UnitPrice * d.Quantity)) > valor)) VB .NET

ctx.Orders.Where(o => o.Order_Details.Sum(d => (d.UnitPrice * d.Quantity)) > valor);

C#

Fazer isso usando SQL iria requerer usar GROUP By mas com Linq to Entities podemos fazer isso de uma forma mais simples.

O código navega a partir dos pedidos para seus detalhes e soma o total de cada um deles; a seguir eles verificam se a soma é maior que o valor informado.

4- Consulta para procurar por pedidos (orders) que incluem o produto na lista de venda.

Neste caso temos que determinar se o produto esta incluído na lista.

O método Any verifica se o produto com o mesmo ID esta no detalhe associado com o pedido e retorna um valor booleano.

ctx.Orders.Where(Function(o) o.Order_Details.Any(Function(d) d.ProductID = productId))
ctx.Orders.Where(o => o.Order_Details.Any(d => d.ProductID == productId));

VB .NET

C#

5 - Aplicando filtros de forma dinâmica

Para aplicar filtros dinamicamente podemos usar a interface IQueryable<T> e no exemplo estamos consultado por cidade, cliente e codigo do produto. Se o cliente não for informado consultamos por codigo de produto caso contrário por cliente.


Dim resultado As IQueryable(Of Order) = ctx.Orders
If Not String.IsNullOrEmpty(cidade) Then
    resultado = result.Where(Function(o) _
    o.Order_Details.Any(Function(d) d.ProductID = codigoProduto))
End If
If Not String.IsNullOrEmpty(cliente) Then
   resultado = result.Where(Function(o) _
   o.Customer.CompanyName = cliente)
End If

IQueryable<Order> result = ctx.Orders;
if (!String.IsNullOrEmpty(cidade))
    result = result.Where(o => o.Order_Details.Any(d => d.ProductID == codigoProduto));
if (!String.IsNullOrEmpty(customerName))
     result = result.Where(o => o.Customer.CompanyName == cliente);

VB .NET

C#

Para realizar a paginação com LINQ to Entities podemos usar os métodos Skip e Take, onde Skip ignora um número n de linhas e Take toma somente n linhas. Veja abaixo exemplos de como usar este recurso:

resultado = resultado.Skip(10).Take(10)
resultado = resultado.Skip(10).Take(10);

VB .NET

C#

O código acima realiza uma consulta que ignora as primeiras 10 linhas e retornas as próximas 10 linhas.

Se você ainda desejar ordenar o resultado de sua consulta poderá usar o método OrderBy conforme exemplo abaixo:


If campoOrdenacao = "cidade" Then
  resultado = resultado.OrderBy(Function(o) o.ShipCity)
ElseIf campoOrdenacao = "endereco" Then
  resultado = resultado.OrderBy(Function(o) o.ShipAddress)
Else
  resultado = resultado.OrderBy(Function(o) o.ShipCountry)
End If
if (campoOrdenacao == "cidade")
  resultado = resultado.OrderBy(o => o.ShipCity);
else if (campoOrdenacao == "endereco")
  resultado = resultado.OrderBy(o => o.ShipAddress);
else
  resultado = resultado.OrderBy(o => o.ShipCountry);

VB .NET

C#

Temos dessa forma uma visão geral de como gerar o nosso modelo de entidades usando Entity Framework e consultar este modelo usando LINQ to Entities para obter informações.

Pegue o projeto completo com os exemplos aqui : Modelo_EF.zip

ASP .NET - O acesso a dados usando um ORM - Entity Framework - III

Referências:


José Carlos Macoratti