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; } }
我理解这个:
-
对于延迟加载 :因为加载是惰性的,如果我调用dbset
dpc_gestion
则不会加载所有导航属性。 这种类型的负载是性能和响应性最好的。 它默认启用,如果我想重新启用它,我必须设置:context.Configuration.ProxyCreationEnabled = true; context.Configuration.LazyLoadingEnabled = true;
-
对于急切加载它不是懒惰的:当我加载
dpc_gestion
时它加载了所有导航属性。 可以使用include
方法加载导航属性。 要启用此加载类型:context.Configuration.LazyLoadingEnabled = false;
-
对于显式加载它就像热切加载,但我们使用
Load
方法而不是include
。
所以我想知道:
- 如果这个小简历是真的吗?
- 如果是真的,那么热切和显式加载有什么区别?
- 如果我使用延迟加载并且我调用例如
dpc_gestion.dpc_participant
,导航属性是否加载?或者我将获得exception? - 是否有一种情况是,在性能和响应性方面,预先加载或显式加载比延迟加载更好?
谢谢
如果这个小简历是真的吗?
是。
如果是真的,那么热切和显式加载有什么区别?
渴望加载与延迟加载相反,但显式加载类似于延迟加载 , 除了:您在代码中显式检索相关数据; 访问导航属性时不会自动执行此操作。 通过获取实体的对象状态管理器条目并为集合调用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
实体编写更多查询。