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

42
我已经阅读了这个教程和这篇文章,但我并不完全理解每种加载类型的用途。

我解释一下

我有这个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,所有导航属性都不会被加载。这种类型的加载在性能和响应速度方面是最好的。它是默认启用的,如果我想重新启用它,我必须设置:

    context.Configuration.ProxyCreationEnabled = true;    
    context.Configuration.LazyLoadingEnabled = true;
    
  2. 对于急切加载 它不是懒加载:在我加载dpc_gestion时,它会加载所有导航属性。可以使用include方法来加载这些导航属性。要启用此加载类型:

  3. context.Configuration.LazyLoadingEnabled = false;
    
  4. 对于显式加载,它类似于急切加载,但我们使用Load方法而不是include

所以我想知道:

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

谢谢


使用 ModelView 是最佳实践。 - Karwan E. Othman
3个回答

42

如果这个简单的简历是真的吗?

是的。

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

急切加载惰性加载的相反,但显式加载惰性加载类似,不同之处在于:你需要在代码中明确地检索相关数据;当你访问导航属性时,它不会自动发生。您可以通过获取实体的对象状态管理器条目,并对集合调用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 请求,而急切加载可以通过单个请求加载数据。 急切加载也是解决ORM中n+1 selects issue的好选择。请参阅此帖子:什么是n+1 selects问题?


1
@LamloumiAfif...还有一件需要注意的事情是:懒加载会产生多个 SQL 请求,而急切加载只需一个请求即可加载数据。 - Salah Akbari
1
此外,设置 LazyLoadingEnabled = false 并不能启用急切加载。它会禁用延迟加载。当启用延迟加载时,可以使用 Include 来进行急切加载。 - Colin
@Colin 是不是只有使用 Include 才能实现急加载? - Andes Lam
@AndesLam 我认为急切加载是在原始查询中预先获取实体 - 这需要使用 Include。您可以在导航属性上调用 Load - 但我不会称之为急切加载 https://learn.microsoft.com/en-us/ef/ef6/fundamentals/relationships#loading-related-objects - Colin
@Colin 我感到困惑的原因是因为在 OP 的话中,“它不是懒加载:当我加载 dpc_gestion 时,它会加载所有导航属性”。这就好像导航属性在急切加载时自动获取。然而,迄今为止我所学到的并没有给我这种印象——include 是唯一的方法,而且它是一个手动过程。 - Andes Lam

11

问题1和2:

你对懒加载急加载的解释是正确的。
显式加载的使用方法有点不同于你描述的。

EntityFramework返回IQueryable对象,它们本质上包含了到数据库的查询。但在第一次枚举之前,这些对象不会被执行。
Load执行查询,以便将其结果存储在本地。
调用Load与调用ToList并丢弃该List相同,但没有创建List的开销。

问题3:

如果使用懒加载,EntityFramework会为您处理navigation property的加载,因此您不会遇到异常。
请记住,这可能需要一段时间,并使应用程序无响应

问题4:

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

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


2
还要记住,对于懒加载,上下文必须是活动的,否则会抛出异常。我会说,在断开连接的情况下,只要您构建将被序列化的对象图,就可以使用懒加载,尽管通常通过“Include”或“Load”显式加载所需的所有内容。 - Gert Arnold

0
在这里,您将学习如何显式地在实体图中加载相关实体。无论是在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 实体的集合导航属性 Courses。 Collection() 方法获取表示集合导航属性的对象。 Load() 方法执行数据库中的 SQL 查询以获取数据并在内存中填充指定的引用或集合属性,如下所示。 enter image description here
Query(): 您还可以编写 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 实体编写进一步的查询。

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