Lazy Loading与Eager Loading比较

117

在什么情况下,贪婪加载可能比惰性加载更有利?

在 Entity Framework 中,惰性加载是默认的加载和访问相关实体的现象。然而,贪婪加载是指强制加载所有这些关系的实践。

我之所以问这个问题,是因为显然惰性加载更节省资源,即使我们使用 ToList() 方法,也可以利用惰性加载行为的优势。

但是,我认为惰性加载会增加实际数据库请求的数量,也许这就是开发人员有时使用 Inlcude 方法来强制加载所有关系的原因。

例如,在 MVC 5 中使用 Visual Studio 自动叉车,控制器中自动创建的 Index 方法总是使用贪婪加载,这一点我一直不明白为什么 Microsoft 在那种情况下默认使用贪婪加载。

如果有人能解释在什么情况下贪婪加载比惰性加载更有优势,并且为什么我们在有比惰性加载更节省资源的东西存在时仍要使用它,我将不胜感激。


9
想象一种情况,当您的数据库上下文被释放时,惰性加载将不再发生。此时,使用急切加载会更有好处。 - Transcendent
2
我见过很多项目因性能问题而失败,这是由于“N + 1 Select”问题导致的,当进行延迟加载时会更快地出现,所以一定要查找相关信息。 - David DV
9个回答

113

我认为将关系分类如下是很好的:

何时使用急切加载

  1. 在一对多关系的“一侧”中,您确定会在主实体的各个地方使用。例如Article的User属性,Product的Category属性。
  2. 通常当关系不太多,且渴望加载将是减少服务器上进一步查询的良好实践。

何时使用延迟加载

  1. 几乎在每个一对多关系的“集合侧”上。例如User的Articles或Category的Products。
  2. 您确切知道不会立即需要某个属性。

注意:正如Transcendent所说,延迟加载可能存在处理问题。


7
当你知道很少需要使用相关数据时,请使用惰性加载(lazy loading)。但是当你知道经常需要某些相关数据时,请使用及早加载(eager loading)。原文意思不变,语言更加通俗易懂。 - Ghasan غسان
我可以同时使用它们吗?例如,如果一个实体与另一个实体几乎相关,我可以通过贪婪加载将其包含进来,而其他相关实体则通过延迟加载。 - Ahmad Alaa

43

预先加载: 预先加载可以一次性加载所有所需实体,即相关对象(子对象)会自动随其父对象一起加载。

何时使用:

  1. 在关系不太多的情况下使用预先加载。 因此,预先加载是减少服务器进一步查询的良好实践。
  2. 当您确定将在各处使用相关实体和主要实体时,请使用预先加载。

延迟加载: 在延迟加载的情况下,相关对象(子对象)不会自动加载, 直到请求时才与其父对象一起加载。 默认情况下,LINQ支持延迟加载。

何时使用:

  1. 当使用一对多集合时,请使用延迟加载。
  2. 当您确定不立即使用相关实体时,请使用延迟加载。

注意:Entity Framework支持三种方式来加载相关数据 - 预先加载、延迟加载和显式加载。


我对急切加载和延迟加载感到非常困惑。你能帮我理解一下吗?通过谷歌的参考,我发现了关于延迟加载的东西。当您想使用延迟加载加载数据时,应使用虚拟关键字。延迟加载是指在第一次访问实体或实体集合时,从数据库自动加载实体或实体集合的过程。这是2016年1月7日的内容,与您所说的延迟加载是否相同? - rykamol
1
@rykamol 试着将其理解为一种设计模式。您可以参考以下链接以更好地理解:Eager Loading - https://www.entityframeworktutorial.net/eager-loading-in-entity-framework.aspx,Lazy Loading - https://www.entityframeworktutorial.net/lazyloading-in-entity-framework.aspx,Explicit Loading - https://www.entityframeworktutorial.net/EntityFramework4.3/explicit-loading-with-dbcontext.aspx - Ghost
6
如果我要求您获取汤姆的个人数据并警告您我可能需要他(部分)子女的个人数据,则您更愿意一次性获取所有汤姆及其子女的数据(主动加载),还是给我汤姆的数据,然后承诺如果我最终需要时将去获取任何一个他孩子的数据(惰性加载)?这两种方法都有自己的好处,惰性加载可以避免加载未使用的数据,但主动加载可以最小化对数据库的访问次数。 - Flater
@Flater 非常感谢您,我不会再忘记了。 - rykamol

32

懒加载会产生多个SQL调用,而贪婪加载可能会使用一个“更重”的调用(带有连接/子查询)一次性加载数据。

例如,如果您的Web服务器和SQL服务器之间存在高延迟,您应该选择贪婪加载,而不是使用懒加载逐个加载相关项目。


我可以同时使用它们吗?例如,如果一个实体与另一个实体几乎相关,我可以通过贪婪加载将其包含进来,而其他相关实体则通过延迟加载。 - Ahmad Alaa

17

急切加载 当您确定同时需要获取多个实体时,例如您需要在同一页上显示用户和用户详细信息,则应选择急切加载。 急切加载将对数据库进行单次访问并加载相关实体。

延迟加载 当您只需在页面上显示用户,并且通过单击用户才需要显示用户详细信息时,您需要使用延迟加载。 延迟加载会进行多次访问以加载相关实体,当您绑定/迭代相关实体时。


16

请考虑以下情况:

public class Person{
    public String Name{get; set;}
    public String Email {get; set;}
    public virtual Employer employer {get; set;}
}

public List<EF.Person> GetPerson(){
    using(EF.DbEntities db = new EF.DbEntities()){
       return db.Person.ToList();
    }
}

现在,在调用这个方法之后,你不能再懒加载Employer实体了。为什么?因为db对象已经被释放了。所以你需要使用Person.Include(x=> x.employer)来强制加载它。


3
是的,这就是Lazy Loading无法帮助的例子。另一件事是每次需要数据时创建DbContext是不好的方式。如果您使用某些IoC容器,您的DbContext将与请求一起存在(对于Web应用程序而言)。 - Miroslav Holec
@MiroslavHolec:太棒了,这正是我使用Ninject的方式。你刚才提到的确实非常非常好。 - Transcendent

6

懒加载 - 在处理分页时非常有用,例如在页面加载时出现一个包含10个用户的列表,当用户向下滚动页面时,API调用会带来接下来的10个用户。当您不想一次性加载整个数据,因为这将需要更多时间并且会给用户带来糟糕的体验时,它是非常有用的。

急加载 - 当没有太多关系并且可以在单个对数据库的调用中获取整个数据时,就像其他人建议的那样,它是非常有用的。


2
连续滚动或分页 != 懒加载。 - Gert Arnold
是的先生,这只是为了清晰起见的一个例子,懒加载基本上是在需要数据的时候才加载/获取数据。 - Iqra Abdul Rauf
1
在Entity Framework的明确和具体的上下文中,这不是惰性加载。 - Gert Arnold
分页是另一回事。 - Zhasulan Berdibekov
分页是另一回事 - undefined

-1

从SEO的角度来看,延迟加载有助于在加载图像之前保留资源,帮助减少加载时间,因此经常用于提高网站加载速度。

然而,有时这会导致Google的“首次内容绘制”(FCP)指标出现延迟,因此建议对横幅图像和“折叠区域”内容使用loading ='eager'。特别是在主页上。


-3
// Using LINQ and just referencing p.Employer will lazy load
// I am not at a computer but I know I have lazy loaded in one
// query with a single query call like below.
List<Person> persons = new List<Person>();
using(MyDbContext dbContext = new MyDbContext())
{
    persons = (
        from p in dbcontext.Persons
        select new Person{
            Name = p.Name,
            Email = p.Email,
            Employer = p.Employer
        }).ToList();
}

1
虽然这段代码片段可能解决了问题,但是包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议的原因。 - He Yifei 何一非
1
这个回答完全没有回应 OP 的问题。OP 不是在问如何进行“惰性加载”,而是在问“何时使用惰性加载和急切加载”。 - Mischa

-3

如果可能的话,最好使用急切加载,因为它可以优化应用程序的性能。

例如:

Eager loading

var customers= _context.customers.Include(c=> c.membershipType).Tolist();

lazy loading

在模型中,客户必须定义

Public virtual string membershipType {get; set;}

因此,在查询时,惰性加载需要加载所有引用对象,但急切加载只查询和选择相关的对象,因此速度更快。


使用性能诊断工具,如Glimpse,并检查在惰性加载时具有多个连接和查询急切地只有一个的情况下两者的工作方式。我已经亲自检查了这些东西,请说明您为什么认为是错误的。 - Nuwan Dhanushka
#FakeCaleb已删除了他的评论。 - Nuwan Dhanushka
一位版主删除了我的评论,由于你误解了我的评论,并且回复也没有理解到重点,我认为继续这个对话没有意义。 - FakeCaleb
我认为你的措辞暗示着急切加载比惰性加载更好,因为它可以提高性能。但是我可以想到一些情况,这并不正确。 - FakeCaleb
当您不需要子对象时........实际上,更好的答案在这里解释了何时使用惰性和何时使用急切。 - FakeCaleb
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接