>我在项目中遇到了一些速度问题,这似乎是它使用实体框架调用数据库的主要原因。每次调用数据库时,它总是按database.Include(...).Where(...)
我想知道这是否与database.Where(...).Include(...)
?
我的想法是,第一种方法包括目标表中所有元素的所有内容,然后过滤掉我想要的元素,而第二种方法过滤掉我想要的元素,然后只包含这些元素的所有内容。我不完全了解实体框架,所以我的想法正确吗?
实体框架尽可能长时间地延迟其查询,直到代码开始处理数据为止。只是为了证明这个例子:
var query = db.People
.Include(p => p.Cars)
.Where(p => p.Employer.Name == "Globodyne")
.Select(p => p.Employer.Founder.Cars);
对于所有这些链式调用,EF 尚未调用数据库。相反,它会跟踪您尝试获取的内容,并且它知道如果您开始使用数据要运行哪个查询。如果在此之后再也不对query
执行任何其他操作,那么您将永远不会访问数据库。
但是,如果您执行以下任一操作:
var result = query.ToList();
var firstCar = query.FirstOrDefault();
var founderHasCars = query.Any();
现在,EF被迫查看数据库,因为它无法回答您的问题,除非它实际从数据库中获取数据。此时,而不是之前,EF 是否实际命中数据库。
作为参考,这个用于获取数据的触发器通常称为"枚举集合",即将查询转换为实际的结果集。
通过尽可能长时间地延迟该查询的执行,EF 能够等待并查看是否要筛选/排序/分页/转换/...结果集,这可能导致 EF 需要返回的数据少于立即执行每个命令时返回的数据。
这也意味着,当您调用Include
时,您实际上并没有命中数据库,因此如果您没有枚举集合,您将不会从稍后将由Where
子句过滤的项目中加载数据。
举这两个例子:
var list1 = db.People
.Include(p => p.Cars)
.ToList() // <= enumeration
.Where(p => p.Name == "Bob");
var list2 = db.People
.Include(p => p.Cars)
.Where(p => p.Name == "Bob")
.ToList(); // <= enumeration
这些列表最终将产生相同的结果。但是,第一个列表将在您过滤数据之前获取数据,因为您在Where
之前调用了ToList
。这意味着您将在内存中加载所有人及其汽车,然后在内存中过滤该列表。
但是,第二个列表仅在已知Where
子句时枚举集合,因此 EF 只会将名为 Bob 的人及其汽车加载到内存中。筛选将在数据库发送回运行时之前在数据库上进行。
您没有显示足够的代码让我验证您是否过早枚举集合。我希望这个答案可以帮助您确定这是否是性能问题的原因。
database.Include(...).Where(...)
,我想知道这是否与database.Where(...).Include(...)
不同?
假设此代码是逐字的(缺少的数据库集除外(,并且在Include
和Where
之间没有任何反应,则订单不会更改执行,因此它不是性能问题的根源。
我通常建议您将Include
语句放在其他任何内容之前(即紧接在db.MyTable
之后(,作为可读性的问题。其他操作取决于您尝试构造的特定查询。
大多数时候,子句的顺序不会有任何区别Include
语句告诉 SQL 一个表与另一个表Join
虽然Where
将导致..是的,SQLWhere
当你做类似database.Include(...).Where(...)
的事情时,你正在构建IQueryable对象,当你尝试像.ToList()
或.FirstOrDefault()
一样访问它并且这些查询已经优化后,该对象将被转换为直接SQL
因此,如果仍然存在性能问题 - 应使用探查器查找瓶颈,并可能考虑使用存储过程(这些存储过程可以与 EF 集成(