[翻译 EF Core in Action 2.4] 加载相关数据
Entity Framework Core in Action
Entityframework Core in action是 Jon P smith
所著的关于Entityframework Core 书籍。原版地址. 是除了官方文档外另一个学习EF Core的不错途径, 书中由浅入深的讲解的EF Core的相关知识。因为没有中文版,所以本人对其进行翻译。 预计每两天一篇更新 PS: 翻译难免限于本人水平有不准确的地方,建议英文水平不错的同学直接查看原版,有不足的地方欢迎指正
第一部分目录导航
加载相关数据
之前我向你展示了Book实体类,它有其他三个实体的导航属性: PriceOffer,Review和BookAuthor. 下面介绍如何使用代码访问这些数据. 你有以下方式加载数据: 贪婪加载,显式加载,选择加载和懒加载(在EF Core 2.1中)
有一点很重要,EF Core默认不会加载实体类的任何关系(导航属性),如果加载了Book类,在默认情况下Book实体的每个导航属性都为null
默认不加载任何关系的行为是正确的,这意味着EF Core最小化了数据库访问. 如果你想加载一个关系则添加需要代码告诉EF Core. 下面介绍三种方法,以及它们的优缺点
贪婪加载: 加载与主实体类的关系
加载相关数据的第一种方法是贪婪加载. 贪婪加载需要告诉EF Core在加载主实体数据的查询中加载导航属性. 使用两个方法Include和ThenInClude指定预先加载. 下个清单展示了Book实体类实例查询Books表的第一行数据以及贪婪加载单个关系Reviews
- EF6 EF Core的贪婪加载与EF 6.x的方法类似, 但EF Core改进了语法和SQL实现. EF 6.x没有ThenInclude方法, 并且EF 6.x的sql实现是尝试在一个查询中加载所有数据和集合,这样的SQL查询很可能是低效的. EF Core在一个单独的查询中加载集合,你可以在前面的SQL部分看到这一点
下面我们看一个更复杂的例子,下面的清单展示了查询第一本书并且贪婪加载它所有的关系.
之前展示了使用贪婪加载方法Include获取AuthorsLink关系(一级关系),直接由加载的实体类引用的关系. Include后面使用ThenInclude加载二级关系,本例中是BookAuthor的Author表. 这种方式(Include后面使用ThenInclude)是访问深层次关系的标准方法,你可以使用更多的ThenInclude深入更深的关系
如果数据不存在,比如Book类的Promotion属性指向的可选PriceOffer类,Include不会失败,它只是加载不到任何东西,如果是集合会返回一个空集合. ThenInclude也是这样. 如果前面的Include或Theninclude为空,后面的ThenInclude会被忽略
贪婪加载的缺点是会加载所有的数据,有时你不需要某些部分数据,例如图书列表不需要书籍描述,而图书描述一般会很长.
注: 在EF Core2.0中,如果你在查询中使用了不必要的Include方法,则会有一个警告,比如 context.Books.Include(b => b.Promotion).Where( b => b.Promotion.NewPrice > 10).Select(b => b.BookId)
代码中使用了include,但它是不必要的,因为查询只返回了BookId. EF Core团队为此情况添加了警告,因为没有必要使用Inlcude方法.
显式加载: 在加载了实体类之后加载关系
第二个方法是显式加载,加载了主实体类之后,使用显式加载其他所需的关系. 下面的代码首先加载了书籍,然后使用显式加载命令读取所有关系
显式加载有一个额外的命令,在加载数据的同时并且可用查询. 下面的代码展示了使用显式加载方法Query命令查询评论数量并加载每个评论的所有星级评分. 你可以在Query方法后使用LINQ命令,例如Where,Orderby等
显式加载的优点是不会立即加载实体的关系. 这对于在某些情况下需要相关数据,显式加载会很有用. 你还会发现在复杂业务逻辑中显式加载也很有用
显式加载的缺点是会产生更多的数据库往返,这可能是低效的. 如果你事先知道所需的数据,那么贪婪加载通常会更好,因为加载关系所需的数据库往返次数更少
选择加载: 加载实体类的特定属性和关系
第三种方法是使用LINQ的Select方法明确的选择需要的数据,我称之为选择加载. 下面展示了使用Select方法从Book类中选择属性,在查询中执行特定的代码获取书的评论数量
Select查询方法的优点是只加载你需要的数据,在你不需要所有的数据时,这会很有用. 上面的查询代码只需要一个Select SQL命令获取所有数据. 在数据库往返次数方面也很少. EF Core将查询的p.Reviews.Count
转换为SQL命令,在数据库中完成计数. 如下面的SQL所示
SELECT TOP(1) [p].[Title], [p].[Price], (
SELECT COUNT(*)
FROM [Review] AS [r0]
WHERE [p].[BookId] = [r0].[BookId] )
FROM [Books] AS [p]
选择加载的缺点是需要为每个属性/计算编写代码. 这会很繁琐, 在10.3节中我介绍了一种自动化的方法
注: 在本章的后面,你会看到更复杂的选择加载案例,我们会使用这种类型的加载构建图书应用程序的图书列表查询
EF Core 2.1中的延迟加载
在EF 6.x中将属性标记为virtual,在读取到该属性时会进行数据库访问. 延迟加载添加到了EF Core 2.1中
喜欢延迟加载的人说,延迟加载很容易使用,在读取属性时不需要应用程序数据库上下文. 延迟加载的缺点是对延迟加载的数据进行更多的数据库访问. 让查询变慢, 本章描述的三种方法在我看来完全可以消除延迟加载的存在意义,它们可以带来性能更高的数据库访问