EF6中的渴望,懒惰和显式加载

时间:2016-01-06 07:34:59

标签: c# entity-framework orm entity-framework-6 lazy-loading

我已阅读此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; }
}

我理解这一点:

  1. 对于延迟加载:因为加载是懒惰的,如果我调用dbset dpc_gestion,则会加载所有导航属性赢了 。这种类型的负载是性能和响应性最好的。它默认启用,如果我想重新启用它,我必须设置:

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

    context.Configuration.LazyLoadingEnabled = false;
    
  3. 用于显式加载 这就像热切加载一样,但我们使用Load方法代替include

  4. 所以我想知道:

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

3 个答案:

答案 0 :(得分:18)

  

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

  

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

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

来自techblog

  

渴望加载:

     

急切加载是 延迟加载的反面,即:进程   加载一组特定的相关对象以及对象   在查询中明确请求的内容。

     

明确加载:

     

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

  

如果我使用延迟加载并调用示例dpc_gestion.dpc_participant,导航属性是否会加载?或者我会得到异常?

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

  

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

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

急切加载通常更有效。而且当关系不是太多时,急切加载将是减少服务器上进一步查询的良好做法。但是当你知道你不会立即需要一个房产时,延迟加载可能是一个不错的选择。而且,如果您的数据库上下文被丢弃并且无法再进行延迟加载,那么急切加载也是一个不错的选择。例如,请考虑以下事项:

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

调用此方法后,您无法懒惰地加载相关实体,因为db已被处理,因此 Eager Loading 将是更好的选择。

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

答案 1 :(得分:6)

问题1和2:

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

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

问题3:

如果您使用延迟加载,EntityFramework将负责为您加载导航属性,因此您不会获得例外。
请注意,这可能需要一段时间才能使应用无响应

问题4:

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

此外,如果您知道自己将使用导航属性,那么将其加载急切是一种很好的做法,因此您不会等到从数据库加载它们 例如,假设您将结果存储在列表中并将其绑定到WPF DataGrid。如果DataGrid访问尚未加载的属性,则在显示该属性之前,用户会遇到明显的超时。此外,应用程序在加载期间不会响应(如果您没有异步加载)。

答案 2 :(得分:1)

在这里,您将学习如何在实体图中显式加载相关实体。 显式加载在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()加载学生实体的集合导航属性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实体编写进一步的查询。