Um tópico muito discutido nos fóruns de acesso a dados (http://social.msdn.microsoft.com/Forums/pt-BR/adoptpt/threads) e LINQ (http://social.msdn.microsoft.com/Forums/pt-BR/linqpt/threads) é o uso de Lazy Load no Entity Framework.
O Lazy Load (ou Lazy Loading) nada mais é do que um mecanismo adotado pelos frameworks de persistência a dados para carregar as informações sobre demanda, isto é, carregar em memória apenas os dados de propriedades que não sejam relacionamentos para outras entidades. Este tipo de recurso minimiza o consumo de memória, o trafego de dados pela rede e os recursos de consulta do banco de dados, além de tornar a consulta mais rápida.
Vamos tomar como exemplo o seguinte cenário: uma construtora possui diversos prédios. Este é um relacionamento bastante simples de um para muitos, onde a construtora (um) possui diversos prédios (muitos) associados. Para esta implementação utilizaremos o Entity Framework Code First versão 5 Beta. Para instalar esta versão rode o comando “Install-Package EntityFramework -Pre” em seu Package Manager Console (http://nuget.org/packages/EntityFramework).
Após instalado, crie em seu projeto as seguintes classes conforme o código abaixo:
public class Construtora { public int Id { get; set; } public string Nome { get; set; } public string RegistroCivil { get; set; } public List<Predio> Predios { get; set; } public Construtora() { this.Predios = new List<Predio>(); } } public class Predio { public int Id { get; set; } public string Nome { get; set; } public Endereco Endereco { get; set; } } [ComplexType()] public class Endereco { public string Logradouro { get; set; } public string Numero { get; set; } public string Bairro { get; set; } public string Cidade { get; set; } public string CEP { get; set; } }
As classes Construtora, Predio e Endereco são auto-explicativas e representam a abstração da essência do problema discutido.
Como previsto, para utilização do Entity Framework Code First precisamos criar uma classe que seja nosso proxy de comunicação com o banco de dados (contexto de banco de dados). Esta classe pode ser criada como a classe Contexto descrita logo abaixo:
public class Contexto : DbContext { public DbSet<Predio> Predios { get; set; } public DbSet<Construtora> Construtoras { get; set; } }
Veja que em nossa classe de contexto (Data Context) não fizemos nenhuma configuração adicional e apenas registramos as entidades que devem ser persistidas na base de dados.
Para realizar os testes com o uso de Lazy Load fiz a inclusão de alguns registros com o seguinte script:
Construtora construtoraA = new Construtora(); construtoraA.Nome = "Construtora A"; construtoraA.RegistroCivil = "22.22.2222-2"; construtoraA.Predios.Add(new Predio() { Nome = "Prédio 01", Endereco = new Endereco() }); construtoraA.Predios.Add(new Predio() { Nome = "Prédio 02", Endereco = new Endereco() }); construtoraA.Predios.Add(new Predio() { Nome = "Prédio 03", Endereco = new Endereco() }); using (Contexto db = new Contexto()) { db.Construtoras.Add(construtoraA); db.SaveChanges(); }
Agora que existem alguns registros na base de dados vamos fazer uma consulta e verificar como os nossos dados são retornados, para isso podemos utilizar uma sintaxe Lambda ou uma sintaxe LINQ como a seguir:
// Lambda using (Contexto db = new Contexto()) { var x = db.Construtoras .Where(c => c.Id > 0); } // LINQ using (Contexto db = new Contexto()) { var x = from c in db.Construtoras select c; }
O resultado da consulta foi o seguinte:
Podemos perceber que mesmo inserindo dados para a propriedade “Predios”, da classe “Construtora”, não obtivemos nenhum registro como resultado de nossa consulta, tanto que durante o debug da aplicação podemos notar a quantidade de 0 registros associados a instância de construtora. Tudo isso por conta do Lazy Load.
Agora, faremos diferente… Vamos forçar o carregamento dos registros associados com as entidades resultantes de nossa consulta que não foram carregados por conta do Lazy Load do Entity Framework. Neste ponto entra em destaque o método Include, que é o responsável pelo carregamento de registros associados as entidades de nossas consultas. Para que tudo funcione corretamente faça referência ao namespace System.Data.Entity. Nosso script de consulta deve ficar como:
// Lambda using (Contexto db = new Contexto()) { var x = db.Construtoras .Include(c => c.Predios) .Where(c => c.Id > 0); } // LINQ using (Contexto db = new Contexto()) { var x = from c in db.Construtoras.Include(c => c.Predios) select c; }
O resultado da segunda consulta foi este:
Podemos notar que por conta do uso do método Include conseguimos fazer o carregamento das demais instâncias de registros associados com nosso escopo de consulta. O recurso de Lazy Load é bastante útil e favorece a construção de consultas mais leves, que consumam menos recursos e sejam mais rápidas.
Fica a dica!
Por
Fernando Henrique Inocêncio Borba Ferreira
Microsoft Most Valuable Professional – Data Platform Development
Olá, eu senti dificuldade na explicação da arquitetura. Quem está aprendendo isso precisa entender a arquitetura do projeto: Exemplo: Teremos o projeto chamado: DAL onde ficara o edmx, teremos o objeto BE onde ficaram nossas entidades code first.. e assim por diante….
Olá Danilo,
Obrigado por seu comentário.
Vou levar esses termos em consideração nos próximos posts.
Obrigado por comentar!
[]s!
Bacana, ficou fácil de entender.Obrigado
Olá Gilson!
Obrigado pelo comentário!
ps: existe um post mais recente que fala sobre outras técnicas de como carregar dados relacionados.
[]s!
Fernando, muito bacana. Porém surgiu uma duvida. Se uma view fortemente tipada precisa-se de uma coleção de prédios (View(db.Construtoras.Include(“Predio”).Where(c => c.Id > 0))). Mas, caso na view eu precise fazer model.Endereco.Nome por exemplo. Nesse caso o predio tem relacionamento com endereco que não foi incluida. E seu fizesse Construtoras.Include(“Predio”).Include(“Endereco”) daria erro pois Construtora não tem relacionamento com Endereco. Como fazer nesses casos?
Olá Anderson,
Nesse caso vc deve fazer algo como:
(View(db.Construtoras.Include(“Predio”).Include(“Predio.Endereco”).Where(c => c.Id > 0))).
Obrigado por postar.
[]s!
Po cara muito obrigado! A tempos penava nisso. Com usa da string dentro do include fica muito mais fácil. 4bs!
Fernando, me ajudou muito mas tive uma dúvida de como adaptar ao que quero fazer.
public ActionResult Details3(long id)
{
var rdm = from c in db.tbRDM.Include(c => c.tbAtividades)
select c;
return View(rdm);
}
quando coloco pra retornar a view, ele diz que estou passando um IQueryable mas preciso passar um objeto tbRDM, que é minha model. Como eu poderia fazer?
Olá Jonatas, tudo beleza?
Como este é o método Details ele espera apenas um item, ao invés de uma coleção de registros. Acredito que vc deva fazer algo como:
public ActionResult Details3(long id)
{
var rdm = (from c in db.tbRDM.Include(c => c.tbAtividades) where c.Id == id select c).FirstOrDefault();
return View(rdm);
}
Espero que ajude.