EF6中的Eager,Lazy和显式加载

我已经阅读了本教程和本文,但我并不完全理解每种加载类型的用法。

我解释

我有这个POCO:

public partial class dpc_gestion { public dpc_gestion() { this.ass_reunion_participant = new HashSet(); this.dpc_participant = new HashSet(); this.dpc_reunion = new HashSet(); } public int dpc_id_pk { get; set; } public Nullable dpc_id_gdp_fk { get; set; } public Nullable dpc_id_theme { get; set; } public int dpc_id_animateur_fk { get; set; } public Nullable dpc_date_creation { get; set; } public Nullable dpc_date_fin { get; set; } public Nullable dpc_date_engag_anim { get; set; } public Nullable dpc_flg_let_engag_anim { get; set; } public Nullable dpc_flg_fsoins_anim { get; set; } public virtual ICollection 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 { get; set; } public virtual ICollection 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

所以我想知道:

  1. 如果这个小简历是真的吗?
  2. 如果是真的,那么热切和显式加载有什么区别?
  3. 如果我使用延迟加载并且我调用例如dpc_gestion.dpc_participant ,导航属性是否加载?或者我将获得exception?
  4. 是否有一种情况是,在性能和响应性方面,预先加载或显式加载比延迟加载更好?

谢谢

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

是。

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

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

来自techblog :

渴望加载:

渴望加载延迟加载相反,它是:加载一组特定相关对象以及查询中显式请求的对象的过程。

明确加载:

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

和:

如果我使用延迟加载并且我调用例如dpc_gestion.dpc_participant ,导航属性是否加载?或者我将获得exception?

您没有任何exception,应加载导航属性。

是否有一种情况是,在性能和响应性方面,预先加载或显式加载比延迟加载更好?

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

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

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

还有一点需要注意: 延迟加载会产生多个SQL请求,而Eager正在加载一个请求加载数据。 渴望加载也是解决ORM中n + 1选择问题的不错选择。 看看这篇文章: 什么是n + 1选择问题?

问题1和2:

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

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

问题3:

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

问题4:

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

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

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

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

 using (var context = new SchoolContext()) { var student = context.Students .Where(s => s.FirstName == "Bill") .FirstOrDefault(); 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查询以获取数据并填充内存中指定的引用或集合属性,如下所示。 在此处输入图像描述
Query():您还可以编写LINQ-to-Entities查询以在加载之前过滤相关数据。 Query()方法使我们能够为相关实体编写更多LINQ查询,以过滤掉相关数据。

 using (var context = new SchoolContext()) { var student = context.Students .Where(s => s.FirstName == "Bill") .FirstOrDefault(); context.Entry(student) .Collection(s => s.StudentCourses) .Query() .Where(sc => sc.CourseName == "Maths") .FirstOrDefault(); } 

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