Entity Framework 6 - Carregando Entidades e Propriedades de Navegação


 Neste artigo vamos abordar as opções disponíveis para carregar dados relacionados no Entity Framework 6 mostrando os prós e contras de cada opção. Vamos tratar os comportamentos Lazy Loading e Eager Loading com exemplos e discutir em quais situações qual a abordagem é mais indicada.

O Entity Framework(EF) fornece um ambiente que permite ao desenvolvedor trabalhar visualmente com classes de entidades que são mapeadas para tabelas, views, stored procedures e relacionamentos de um banco de dados relacional.

O comportamento padrão do EF é carregar somente as entidades diretamente necessárias pela sua aplicação, e, de forma geral, é este comportamento que você deseja. Se o EF carregasse todas as entidades relacionadas através de uma ou mais associações, você provavelmente, carregaria mais entidades do que precisa, impactando a memória usada e o desempenho de sua aplicação.

Você pode controlar quando a carga de entidades relacionadas ocorre e assim otimizar o número de consultas executadas contra o banco de dados. Gerenciando com cuidado se, e quando as entidades relacionadas são carregadas, você pode aumentar o desempenho de sua aplicação e obter um maior controle sobre os seus dados.

Vamos começar falando sobre o comportamento padrão do EF, o Lazy Loading.

Lazy Load é o mecanismo utilizado pelos frameworks de persistência para carregar informações sob demanda. Esse mecanismo torna as entidades mais leves, pois suas associações são carregadas apenas no momento em que o método que disponibiliza o dado associativo é chamado.

Assim quando objetos são retornados por uma consulta, os objetos relacionados não são carregados ao mesmo tempo, ao invés, eles são carregados automaticamente quando a propriedade de navegação for acessada. 

Recursos usados

Definindo o modelo de dados

Neste artigo eu vou usar o Entity Framework 6 e a abordagem DataBase First e neste caso vamos usar o banco de dados Macoratti.mdf do SQL Server 2012 Express.

Podemos usar as seguintes abordagem com o Entity Framework :
    1 - Code First - Criamos classes POCO que são mapeadas para as entidades;
    2 - Database First - Mapeamos para um Banco de dados que já existe;
    3 - Model First -  Criamos o Modelo conceitual primeiro e depois é gerado o script para criar o banco de dados;

Neste banco de dados vamos usar as tabelas Clientes, ClienteTipos e ClienteEmails que possuem as seguintes estruturas:

 Clientes
ClienteTipos
ClienteEmails

Temos aqui um relacionamento Um-para-Muitos entre a tabela Clientes e ClienteEmails e entre a tabela ClienteTipos e Clientes:

Criando o Entity Data Model

Abra O Visual Studio 2013 Express for Windows desktop e clique em Visual Studio Solutions e a seguir em Blank Solution;

Informe o nome da solução como EF6_LazyLoading e clique no botão OK;

Agora vamos criar um novo projeto em nossa solução. Clique no menu FILE e a seguir em Add -> New Project;

Selecione a linguagem Visual Basic e o template Console Application e informe o nome EF6_LazyLoadDemo.

Vamos criar um Entity Data Model no projeto Console. Selecione o projeto e Clique em  PROJECT -> Add New Item;

Selecione a guia Data e o template ADO .NET Entity Data Model, informe o nome Macoratti e clique no botão Add;       

No assistente selecione a opção EF Designer from database e clique no botão Next>;

Selecione a conexão com o banco de dados Macoratti.mdf (Se ele ainda não existir clique em New Connection e defina a conexão);

Aceite as configurações do assistente que salva a string de conexão no arquivo App.Config e cria o contexto MacorattiEntities;

Clique no botão Next>;

Selecione a opção Entity Framework 6.x e clique no botão Next>;

A seguir selecione as tabelas do banco de dados e marque as opções conforme a figura abaixo clicando no botão Finish para concluir essa etapa:

Será gerado o modelo de entidades conforme a figura abaixo:

Temos aqui as entidades Cliente, ClienteTipo e ClienteEmail mapeadas para as respectivas tabelas.

Note que no Entity Data Model - Cadastro.edmx podemos ver as classes :

Carregando dados de navegação : Lazy Loading e Eager Loading

Agora vamos definir o código no Módulo do projeto console conforme mostrado abaixo:

Module Module1
    Sub Main()
        Using context = New MacorattiEntities()
            Dim clientes = context.Clientes
            Console.WriteLine("Somente informação dos clientes")
            Console.WriteLine("======================================")
            For Each cliente In clientes
                Console.WriteLine("O nome do cliente é : {0}", cliente.clienteNome)
            Next
            Console.WriteLine("")
            Console.WriteLine("")
            Console.WriteLine("Solicitando informação de entidades relacionadas")
            Console.WriteLine("clienteTipoDescricao e clienteEmail")
            Console.WriteLine("O  EF gera consultas separadas para cada entidade")
            Console.WriteLine("======================================")
            For Each cliente In clientes
                Console.WriteLine("{0} é um cliente {1}, seu(s) email(s) : ", cliente.clienteNome, cliente.ClienteTipos.clienteTipoDescricao)
                For Each email In cliente.ClienteEmails
                    Console.WriteLine(vbTab & "{0}", email.clienteEmail)
                Next
            Next
            Console.WriteLine("")
            Console.WriteLine("")
            Console.WriteLine("Nesta consulta a consulta não vai ser feita no banco de dados ")
            Console.WriteLine("O EF irá retornar os dados relacionados da memória feita na consulta anterior ")
            Console.WriteLine("======================================")
            For Each cliente In clientes
                Console.WriteLine("{0} é um cliente {1}, seu(s) email(s) ", cliente.clienteNome, cliente.ClienteTipos.clienteTipoDescricao)
                For Each email In cliente.ClienteEmails
                    Console.WriteLine(vbTab & "{0}", email.clienteEmail)
                Next
            Next
        End Using
        Console.ReadKey()
    End Sub
End Module

Executando este código iremos obter o seguinte resultado:

A seguir temos a versão deste código para linguagem C# :

public static void Main()
{
      using (context == new MacorattiEntities()) 
      {
           var clientes = context.Clientes;
           Console.WriteLine("clientes");
           Console.WriteLine("=========");
                foreach (var cliente in clientes) 
                {
		Console.WriteLine("O nome do cliente é : {0}", cliente.clienteNome);
	   }
	   foreach (var cliente in clientes) 
                {
	         Console.WriteLine("{0} é um cliente {1}, seu(s) email(s) : ", cliente.clienteNome, cliente.ClienteTipos.clienteTipoDescricao);
	          foreach (var email in cliente.ClienteEmails) 
                       {
	             Console.WriteLine("\t{0}", email.clienteEmail);
                       }
	   }
                foreach (var cliente in clientes) 
                {
	        Console.WriteLine("{0} é um cliente {1}, seu(s) email(s) ", cliente.clienteNome, cliente.ClienteTipos.clienteTipoDescricao);
	          foreach (void email in cliente.ClienteEmails) 
                       {
		   Console.WriteLine("\t{0}", email.clienteEmail);
     	          }
               }
       }
 }

Agora vamos entender como isso funciona :

- Por padrão o EF carrega somente as entidades que você requisita. Isso é conhecido como lazy loading;

- A alternativa ao lazy loading é conhecida como eager loading e consiste em carregar a entidade pai e cada entidade associada a ela;

- A abordagem eager loading pode carregar uma grande quantidade de objetos na memória causando impactando o desempenho da sua aplicação;

- Neste exemplo começamos executando uma consulta contra a entidade Clientes para carregar todos os clientes:

            Dim clientes = context.Clientes
            Console.WriteLine("Somente informação dos clientes")
            Console.WriteLine("======================================")
            For Each cliente In clientes
                Console.WriteLine("O nome do cliente é : {0}", cliente.clienteNome)
            Next

Esta consulta não é executada imediatamente; ele é executada quando enumeramos a entidade cliente na laço foreach (cliente.ClienteNome) . Esse comportamento é conhecido como princípio da execução deferida.

Na primeira construção do foreach, estamos solicitando dados apenas da tabela Clientes. Não solicitamos dados das demais tabelas. Neste caso o EF somente consulta a tabela Clientes.

Na segunda consulta estamos explicitamente referenciando a propriedade clienteTipoDescricao da entidade ClienteTipos e a propriedade ClienteEmail da entidade ClienteEmails:

            For Each cliente In clientes
                Console.WriteLine("{0} é um cliente {1}, seu(s) email(s) : ", cliente.clienteNome, cliente.ClienteTipos.clienteTipoDescricao)
                For Each email In cliente.ClienteEmails
                    Console.WriteLine(vbTab & "{0}", email.clienteEmail)
                Next
            Next

Acessando diretamente essas propriedade faz com que o EF gere uma consulta para cada tabela relacionada para requisitar os dados.

É importante você entender que o EF neste caso gera uma consulta separada na primeira vez que as tabelas relacionadas são acessadas.

Uma vez que a consulta tenha sido chamada para uma propriedade de uma entidade relacionada, o Entity Framework vai marcar a propriedade como carregada e irá recuperar os dados da memória na próxima execução.

Esta consulta separada para cada tabela funciona bem quando um usuário está navegando em seu aplicativo e requisitando diferentes elementos de dados, dependendo das suas necessidades no momento. Ela pode melhorar o tempo de resposta das aplicações, uma vez que os dados são recuperados, conforme necessário, com uma série de pequenas consultas, em oposição ao carregar um grande volume de dados, e potencialmente causar um atraso no processamento da massa de dados.

Esta abordagem não é adequada quando você precisa de uma grande quantidade de dados das tabelas relacionadas. Neste caso, a abordagem eager loading é melhor pois ela retornar todos os dados em uma única consulta.

A última consulta demonstra que uma vez que uma propriedade filha seja carregada o EF irá retornar os valores da memória e não irá fazer a mesma consulta contra o banco de dados:

         For Each cliente In clientes
                Console.WriteLine("{0} é um cliente {1}, seu(s) email(s) ", cliente.clienteNome, cliente.ClienteTipos.clienteTipoDescricao)

                For Each email In cliente.ClienteEmails
                    Console.WriteLine(vbTab & "{0}", email.clienteEmail)
                Next
         Next

Na próxima parte do artigo irei mostrar como salvar dados de entidades relacionadas.

Na palavra da verdade, no poder de Deus, pelas armas da justiça, à direita e à esquerda,
Por honra e por desonra, por infâmia e por boa fama; como enganadores, e sendo verdadeiros;
Como desconhecidos, mas sendo bem conhecidos; como morrendo, e eis que vivemos; como castigados, e não mortos;
Como contristados, mas sempre alegres; como pobres, mas enriquecendo a muitos; como nada tendo, e possuindo tudo.

2 Coríntios 6:7-10

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti