EF6 中的 Eager 、 Lazy 和显式加载

Posted

技术标签:

【中文标题】EF6 中的 Eager 、 Lazy 和显式加载【英文标题】:Eager , Lazy and explicit loading in EF6 【发布时间】:2016-04-10 05:35:19 【问题描述】:

我已阅读此tutorial 和此article,但我不完全了解每种加载类型的使用。

我解释

我有这个 POCO:

public partial class dpc_gestion

    public dpc_gestion()
    
        this.ass_reunion_participant = new HashSet<ass_reunion_participant>();
        this.dpc_participant = new HashSet<dpc_participant>();
        this.dpc_reunion = new HashSet<dpc_reunion>();
    

    public int dpc_id_pk  get; set; 
    public Nullable<int> dpc_id_gdp_fk  get; set; 
    public Nullable<int> dpc_id_theme  get; set; 
    public int dpc_id_animateur_fk  get; set; 
    public Nullable<System.DateTime> dpc_date_creation  get; set; 
    public Nullable<System.DateTime> dpc_date_fin  get; set; 
    public Nullable<System.DateTime> dpc_date_engag_anim  get; set; 
    public Nullable<bool> dpc_flg_let_engag_anim  get; set; 
    public Nullable<bool> dpc_flg_fsoins_anim  get; set; 
    public virtual ICollection<ass_reunion_participant> ass_reunion_participant  get; set; 
    public virtual theme_dpc theme_dpc  get; set; 
    public virtual gdp_groupe_de_pair gdp_groupe_de_pair  get; set; 
    public virtual ICollection<dpc_participant> dpc_participant  get; set; 
    public virtual ICollection<dpc_reunion> dpc_reunion  get; set; 

我明白了:

    对于延迟加载:因为加载是延迟的,如果我调用 dbset dpc_gestion 所有导航属性将不会加载。这种类型的加载在性能和响应能力方面是最好的。它默认启用,如果我想重新启用它,我必须设置:

    context.Configuration.ProxyCreationEnabled = true;    
    context.Configuration.LazyLoadingEnabled = true;
    

    用于急切加载 它并不懒惰:当我加载 dpc_gestion 时,它加载了所有导航属性。导航属性可以使用include 方法加载。要启用此加载类型:

    context.Configuration.LazyLoadingEnabled = false;
    

    用于显式加载 这就像急切加载,但我们使用Load 方法而不是include

所以我想知道:

    如果这个小简历是真的? 如果是真的,急切加载和显式加载有什么区别? 如果我使用延迟加载并调用例如dpc_gestion.dpc_participant,导航属性会加载吗?还是会出现异常? 是否存在急切加载或显式加载在性能和响应方面优于延迟加载的情况?

谢谢

【问题讨论】:

使用 ModelView 是最佳实践 【参考方案1】:

如果这个小简历是真的?

是的。

如果是真的,急切加载和显式加载有什么区别?

急切加载延迟加载相反,但显式加载类似于延迟加载除了:您在代码中显式检索相关数据;当您访问导航属性时,它不会自动发生。您可以通过获取实体的对象状态管理器条目并为集合调用 Collection.Load 方法或为包含单个实体的属性调用 Reference.Load 方法来手动加载相关数据。

来自techblog:

渴望加载:

急切加载是 延迟加载相反的,后者是:过程 加载一组特定的相关对象以及对象 查询中明确要求的。

显式加载:

显式加载定义为:当查询返回对象时, 相关对象不会同时加载。默认情况下,它们是 在使用 Load 方法明确请求之前不加载 导航属性。

还有:

如果我使用延迟加载并调用例如dpc_gestion.dpc_participant,导航属性会加载吗?还是会出现异常?

您没有遇到任何异常,并且应该会加载导航属性。

是否存在急切加载或显式加载更好的情况 与延迟加载相比性能和响应能力?

当您需要主表的所有检索行的相关数据时,

预加载通常更有效。而且当关系不是太多时,渴望加载将是减少服务器上进一步查询的好习惯。但是,当您知道您不会立即需要某个属性时,延迟加载 可能是一个不错的选择。在您的数据库上下文将被处理并且不再发生延迟加载的情况下,急切加载也是一个不错的选择。例如考虑以下内容:

public List<Auction> GetAuctions()

    using (DataContext db = new DataContext())
    
        return db.Auctions.ToList();
    

调用此方法后,由于db已被释放,因此无法延迟加载相关实体,因此这里急切加载会是更好的选择。

还有一点需要注意的是:延迟加载会产生多个 SQL 请求,而急切加载会通过一个请求加载数据。 Eager loading 也是解决 ORM 中 n+1 选择问题 的不错选择。 看看这个帖子:What is the n+1 selects issue?

【讨论】:

@LamloumiAfif...还有一点需要注意的是:延迟加载会产生多个SQL请求,而Eager loading会产生一个请求加载数据。 另外,设置LazyLoadingEnabled = false 不会启用预加载。它禁用延迟加载。启用延迟加载时,可以使用 Include 进行预加载 @Colin 急切加载的唯一方法是使用 Include 是真的吗? @AndesLam 我将预加载定义为在原始查询中预先获取实体——我认为这需要Include。您可以在导航属性上调用 Load - 但我不会称之为急切加载 docs.microsoft.com/en-us/ef/ef6/fundamentals/… @Colin 我感到困惑的原因是因为用 OP 的话来说,“它并不懒惰:它在我加载 dpc_gestion 时加载了所有导航属性”。就好像在预先加载时自动获取导航属性一样。然而,到目前为止我所学到的并没有给我那种印象——包含是唯一的方法,它是一个手动过程。【参考方案2】:

问题 1 和 2:

您对延迟加载急切加载的解释是正确的。显式加载的使用与您描述的有点不同。

EntityFramework 返回IQueryable 对象,这些对象本质上包含对数据库的查询。但是这些直到第一次被枚举时才会执行。Load 执行查询,以便将其结果存储在本地。 调用Load 与调用ToList 并丢弃List 相同,无需创建List 的开销。

问题 3:

如果您使用延迟加载,EntityFramework 将负责为您加载导航属性,因此您不会遇到异常。 请记住,这可能需要一段时间并使您的应用程序无响应

问题 4:

在断开连接的情况下(例如网络应用程序),您不能使用延迟加载,因为这些对象被转换为 DTO,然后EntityFramework 不会跟踪。

此外,如果您知道要使用 导航属性,最好急切地加载它,这样您就不必等到他们从数据库中加载。 例如,假设您将结果存储在列表中并将其绑定到 WPF DataGrid。如果 DataGrid 访问尚未加载的属性,则用户会遇到明显的超时,直到显示该属性。此外,应用程序在加载期间不会响应(如果您不异步加载)。

【讨论】:

还要记住,对于延迟加载,上下文必须是活动的,否则会抛出异常。我想说的是,在断开连接的情况下,您可以使用延迟加载,只要您正在构建将要被序列化的对象图,尽管通过@ 显式加载您需要的所有内容更为常见987654330@ 或 Load.【参考方案3】:

在这里,您将学习如何在实体图中显式加载相关实体。 显式加载在 EF 6 和 EF Core 中均有效。

即使禁用延迟加载(在 EF 6 中),仍然可以延迟加载相关实体,但必须通过显式调用来完成。使用Load() 方法显式加载相关实体。请考虑以下示例。

using (var context = new SchoolContext())

     var student = context.Students
                              .Where(s => s.FirstName == "Bill")
                             .FirstOrDefault<Student>();

     context.Entry(student).Reference(s => s.StudentAddress).Load(); 
     // loads StudentAddress
     context.Entry(student).Collection(s => s.StudentCourses).Load(); 
     // loads Courses collection      

在上面的示例中,context.Entry(student).Reference(s =&gt; s.StudentAddress).Load() 加载了StudentAddress 实体。 Reference() 方法用于获取指定引用导航属性的对象,Load() 方法显式加载。

同样,context.Entry(student).Collection(s =&gt; s.Courses).Load() 加载 Student 实体的集合导航属性 Courses。 Collection() 方法获取一个表示集合导航属性的对象。

Load()方法在数据库中执行SQL查询,获取数据并填充内存中的指定引用或集合属性,如下图。 查询(): 您还可以编写 LINQ-to-Entities 查询以在加载前过滤相关数据。 Query() 方法使我们能够为相关实体编写进一步的 LINQ 查询以过滤掉相关数据。

using (var context = new SchoolContext())

    var student = context.Students
                    .Where(s => s.FirstName == "Bill")
                    .FirstOrDefault<Student>();

    context.Entry(student)
             .Collection(s => s.StudentCourses)
               .Query()
            .Where(sc => sc.CourseName == "Maths")
            .FirstOrDefault();
     

在上面的示例中,.Collection(s =&gt; s.StudentCourses).Query() 允许我们为StudentCourses 实体编写更多查询。

【讨论】:

以上是关于EF6 中的 Eager 、 Lazy 和显式加载的主要内容,如果未能解决你的问题,请参考以下文章

FetchType.LAZY和FetchType.EAGER什么区别

Fetch Type LAZY 仍然会导致 Eager 加载 Hibernate Spring 数据

Hibernate中load()和get()的区别,lazy加载和Eager加载的区别

Singleton lazy vs eager instantiation

[Hibernate] - EAGER and LAZY

SQLAlchemy lazy load和eager load