EF6 中的急切、延迟和显式加载

Eager , Lazy and explicit loading in EF6

我读过这个tutorial and this 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; }
}

我明白了:

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

    context.Configuration.ProxyCreationEnabled = true;    
    context.Configuration.LazyLoadingEnabled = true;
    
  2. 预加载 它并不懒惰:它在我加载 dpc_gestion 时加载了所有导航属性。可以使用 include 方法加载导航属性。要启用此加载类型:

    context.Configuration.LazyLoadingEnabled = false;
    
  3. 对于显式加载 这就像预先加载,但我们使用 Load 方法而不是 include.

所以我想知道:

  1. 如果这份小简历是真的?
  2. 如果是真的,预加载和显式加载有什么区别?
  3. 如果我使用 延迟加载 并且我调用例如 dpc_gestion.dpc_participant,导航属性是否加载?或者我会得到一个异常?
  4. 是否存在预加载或显式加载在性能和响应能力方面优于延迟加载的情况?

谢谢

问题1和2:

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

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

的开销

问题三:

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

问题四:

在断开连接的情况下(例如网络应用程序),您不能使用延迟加载,因为这些对象被翻译成 DTO,然后未被 EntityFramework.

跟踪

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

If this small resume is true ?

是的。

If it is true, what is the difference between eager and explicit loading?

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

来自 techblog:

Eager Loading :

Eager loading is the opposite of Lazy loading which is : The process of loading a specific set of related objects along with the objects that were explicitly requested in the query.

Explicit Loading :

Explicit loading is defined as : when objects are returned by a query, related objects are not loaded at the same time. By default, they are not loaded until explicitly requested using the Load method on a navigation property.

并且:

If I Use lazy loading and I call for example dpc_gestion.dpc_participant, does the navigation properties loads?or I will get an exception?

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

Is there a case when eager loading or explicit loading were better than lazy loading in performance and responsiveness?

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

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

public List<Auction> GetAuctions()
{
    using (DataContext db = new DataContext())
    {
        return db.Auctions.ToList();
    }
}

调用此方法后,您将无法延迟加载相关实体,因为 db 已被释放,因此 Eager Loading 将是更好的选择。

还有一点要注意:懒加载会产生多个SQL请求,而预加载会产生一个请求要求。 Eager loading 也是解决 ORM 中 n+1 selects issue 的好选择。 看看这个 post:What is the n+1 selects issue?

在这里您将学习如何在实体图中显式加载相关实体。 显式加载 在 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 => s.StudentAddress).Load() 加载了 StudentAddress 实体。 Reference()方法用于获取指定引用导航的对象属性,Load()方法显式加载。

以同样的方式,context.Entry(student).Collection(s => s.Courses).Load() 加载 Student 实体的集合导航 属性 课程。 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 => s.StudentCourses).Query() 允许我们为 StudentCourses 实体编写进一步的查询。