Entity Framework - Usando Lazy Load e Eager Load


Este série de artigos é dedicada ao Entity Framework e tem o objetivo de dar uma introdução básica e uma visão geral de como usar este importante recurso da plataforma .NET. Os artigos são parcialmente baseados na documentação da MSDN de onde são citadas as respectivas referências.

Iniciando com o Entity Framework

Conceito

A ADO .NET Entity Framework foi projetado para permitir que a criação de aplicações com acesso a dados use o modelo de programação feito contra um modelo conceitual ao invés do antigo modelo de programação feito diretamente contra um banco de dados relacional. O objetivo é diminuir a quantidade de código e o tempo de manutenção necessária exigida nestas aplicações orientada a dados.

A ADO .NET Entity Framework é uma framework que abstrai o esquema de um banco de dados relacional e o apresenta como um modelo conceitual. Abaixo a figura que representa a arquitetura em camadas do Entity Framework: (Até o lançamento do VS 2010)

Data Source :  Representa a base da arquitetura onde estão armazenados os dados;

Data Providers :  Os dados são acessados por um ADO.NET data provider.  Até o momento tanto o SQL Server como o MySQL são suportados e provavelmente em breve os principais RDBMS do mercado (Oracle, MySQL, DB2, Firebird, Sybase, VistaDB, SQLite, ...) terão um provider para o EF.

Entity Data Model (EDM) :  O  Entity Data Model  é constituído de 3 partes:

  • Conceptual schema definition language (CSDL) : Declara e define entidades, associações, herança, etc,. As Entity classes são geradas a partir deste esquema;
  • Store schema definition language (SSDL) : Metadados que descreve o container de armazenamento (=banco de dados) que persiste os dados;
  • Mapping specification language (MSL) : Mapeia as entidades no arquivo CSDL para tabelas descritas no arquivo SSDL;

Entity Client : O EntityClient é um provedor gerenciado ADO.NET que suporta o acesso a dados descritos no EDM. Ele é similar ao SQLClient, e fornece diversos componentes como EntityCommand, EntityConnection e EntityTransaction;

Object Services : Este componente permite realizar consultas, atualizações, inclusões e exclusões nos dados, expressados como objetos CLR fortemente tipados que são instâncias de entity types. Ele da suporte tanto a consultas Entity SQL como LINQ to Entities;

Entity SQL (ESQL) : É uma derivação da Transact-SQL, projetada para consultar e manipular entidades definidas no EDM. Ele dá suporte herança e associação sendo que tanto os componentes Object Services como os componentes Entity Client podem executar instruções Entity SQL;

LINQ to Entities : É uma linguagem de consulta fortemente tipada para consultar entidades definidas no EDM;
 

Quando você esta aprendendo uma nova forma de tratar os dados através do mapeamento objeto relacional um ponto importante a considerar e saber como manipular os dados.

Existem duas formas básicas de manipular dados usando o  Entity Framework, são elas:

Neste artigo eu vou tratar apenas da segunda opção.

Vamos começar criando um projeto e gerando um Entity Data Model:

Abra o Visual Studio 2008 com SP1 e crie um novo projeto do tipo WIndows Forms Application com o nome EF_ORM;

A seguir no menu Project selecione a opção Add New Item e em templates escolha ADO .NET Entity Data Model informando o nome Northwind.edmx;

Siga o assistente e selecione o item : Generate From Database e clique em Next>;

Ainda no assistente selecione a conexão com o banco de dados Northwind.mdf e aceite o nome padrão conforme a figura abaixo:

A seguir selecione as tabelas Employees, EmployeeTerritories e Territories , aceite nome padrão para o modelo e clique em Finish;

O modelo conceitual de entidades gerado no mapeamento será apresentado graficamente conforme figura abaixo:

Todo esse trabalho que tivemos foi necessário apenas para gerar o Entity Data Model que representa o modelo conceitual das nossas entidades. Vejamos agora como manipular as informações através desse modelo.

Vamos começar com a tarefa básica de acessar e obter dados.

No formulário padrão criado inclua um controle Combobox (cboEmployees) e um controle ListBox (lstTerritories). Veja abaixo leiaute do formulário como ficou:

Nosso objetivo será carregar o controle Combobox com os nomes dos empregados (Employees) e conforme um nome for selecionado vamos exibir os respectivos territórios  relacionados no controle ListBox.

Agora vamos definir o código usado para acessar os dados usando o modelo de entidades. Primeiro vamos declarar a variável dc como sendo do tipo do container das entidades ou  do tipo ObjectContext, isso irá permitir  o acesso a todas as instâncias das entidades para o Entity Data Model criado:

No Entity Framework, instâncias de ObjectContext encapsulam a conexão com o banco de dados (EntityConnection), o metadados que descreve o modelo (MetadataWorkspace) e um gerenciador de objetos que rastreia alterações e mantém cache de objetos persistidos (ObjectStateManager). Cada objeto retornado por uma consulta  seja usando LINQ to Entities ou Entity SQL será automaticamente anexado a um ObjectContext.

Dim dc As NorthwindEntities

A seguir no evento Load do formulário vamos incluir o código que irá carregar o controle Combobox conforme abaixo:

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        dc = New NorthwindEntities
        Dim consulta = From emp In dc.Employees _
                               Select emp
        cboEmployees.DisplayMember = "FirstName"
        cboEmployees.ValueMember = "EmployeeID"
        cboEmployees.DataSource = consulta.ToList
    End Sub

O código cria uma nova instância para o ObjectContext e efetua uma consulta retornando todos os objetos da entidade Employees;

Em seguida configuramos o controle Combobox para exibir o primeiro nome(FirstName) dos empregados retornados e tratar o valor selecionado pelo código do empregado (EmployeeID);

Agora vamos exibir os territórios relacionados com o empregado selecionado.

Para isso vamos usar o evento SelectedIndexChanged do controle Combobox de forma que feita uma seleção no controle o evento será disparado.

Neste evento temos que colocar o código que vai exibir os territórios do empregado. Veja abaixo como ficou este código:

 Private Sub cboEmployees_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
                                                                             Handles cboEmployees.SelectedIndexChanged

        Dim id As Integer = cboEmployees.SelectedValue
        Dim empregado  = (From emp In dc.Employees _
                                    Where emp.EmployeeID = id _
                                    Select emp).First
        empregado.Territories.Load()

        lstTerritories.Items.Clear()
     
        For Each territorio In empregados.Territories
            lstTerritories.Items.Add(territorio.TerritoryDescription)
        Next
    End Sub

Agora preste atenção neste código !!!

- Obtive o id referente ao código do empregado selecionado na combobox:   Dim id As Integer = cboEmployees.SelectedValue

- Em seguida eu efetuei uma consulta que irá retornar um único objeto do tipo Employees com o código do empregado selecionado;

 Dim consulta = (From emp In dc.Employees _
                       Where emp.EmployeeID = id _
                       Select emp).First

- Depois eu carrego os territórios para o objeto empregado selecionado:     empregado.Territories.Load()

Temos aqui o que é conhecido como Lazy Load , ou em outras palavras : "se o programador não solicitar a carga, o relacionamento entre entidades não será recuperado."

Lazy Load é o mecanismo utilizado pelos frameworks de persistência para carregar informações sobre 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.  É também conhecido como "lazy loading".

Você pode comprovar que isso é verdade tentando contar o número de territórios para o cliente selecionado antes de chamar o Load para o objeto empregado:

lstTerritories.Items.Add(empregado.Territories.Count)

O resultado apontado será sempre zero.

Logo o Entity Framework nesta versão não suporta o Lazy Loading, para isso temos que chamar o Load para o objeto.

Você pode verificar se uma coleção de objetos está carregada utilizando a propriedade :  IsLoaded”. Para o nosso exemplo ficaria assim:

If (Not empregado.Territories.IsLoaded) Then

      empregado.Territories.Load()

End If

Finalmente percorremos cada objeto territorio para o objeto empregado e exibimos o nome do território:

     For Each territorio In empregados.Territories
            lstTerritories.Items.Add(territorio.TerritoryDescription)
        Next

O resultado pode ser visto na tela a seguir:

Uma outra forma de obter o mesmo resultado seria usar o que é conhecido como Eager Load.

Eager Load é o mecanismo pelo qual uma associação, coleção ou atributo é carregado imediatamente quando o objeto principal é carregado.

No Entity Framework o eager load é obtido explicitamente por meio da função Include

Para o nosso exemplo o código ficaria assim:

    Private Sub cboEmployees_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
                                                                              Handles cboEmployees.SelectedIndexChanged

        Dim id As Integer = cboEmployees.SelectedValue
        Dim empregado = (From emp In dc.Employees.Include("Territories") _
                                    Where emp.EmployeeID = id _
                                    Select emp).First
        lstTerritories.Items.Clear()
        lstTerritories.Items.Add(empregado.Territories.Count)
        For Each territorio In empregado.Territories
            lstTerritories.Items.Add(territorio.TerritoryDescription)
        Next
    End Sub

 

Neste exemplo a coleção de objetos Territories é carregado em um único SELECT através da função Include :

        Dim empregado = (From emp In dc.Employees.Include("Territories") _
                                    Where emp.EmployeeID = id _
                                    Select emp).First

No eager load é preciso informar o caminho completo das associações que você deseja recuperar caso contrário os objetos não serão carregados.

Nota: Não é o caso do nosso exemplo mas se tivéssemos gerado o modelo completo para o banco de dados Northwind para carregar as regiões usando o eager load faríamos assim :

  Dim empregado = (From emp In dc.Employees.Include("Territories.Region") _
                                    Where emp.EmployeeID = id _
                                    Select emp).First
 

Considere isso quando você pretender usar eager load explicitamente.

Pegue o projeto completo aqui: EF_ORM.zip

Eu sei é apenas Entity Framework  mas eu gosto...

Referências:


José Carlos Macoratti