我知道IQueryable
和IEnumerable
是如何工作的。但是今天,当我用一个例子重新审视这些主题时。
以下是我的问题
进行了多少次数据库调用?
如果我是对的,有 2 个数据库调用,一个带有where
子句,另一个在使用Take(1)
时调用。
public void GetEmployeesByDept(long deptId)
{
IQueryable<EmployeeDetails> empDetails = _context.EmployeeDetails.Where(x => x.Idseq == deptId);
// First DB call
var firstEmployee = empDetails.Take(1);
// Second DB call
Console.WriteLine(empDetails.GetType());
}
这是我的解释 - 但是当我将鼠标悬停在empDetails
上时,我可以看到表达式包含两个参数
With the table
$x.Idseq == .Constant<TestProject.Program+<>c__DisplayClass2_0>(TestProject.Program+<>c__DisplayClass2_0).deptId
所以,现在当执行完成firstEmployee
时,我将鼠标悬停在firstEmployee
变量上,我可以看到表达式如下
这也是数据库调用吗?
.Call System.Linq.Queryable.Take(
.Call System.Linq.Queryable.Where(
.Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Employee.Models.EmployeeDetails]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Employee.Models.EmployeeDetails]),
'(.Lambda #Lambda1<System.Func`2[Employee.Models.EmployeeDetails,System.Boolean]>)),
1).Lambda #Lambda1<System.Func`2[Employee.Models.EmployeeDetails,System.Boolean]>(Employee.Models.EmployeeDetails $x)
{
$x.Idseq == .Constant<TestProject.Program+<>c__DisplayClass2_0>(TestProject.Program+<>c__DisplayClass2_0).deptId
}
如果这是两者中唯一的数据库调用,那么我为什么能够加载 empDetails 的数据?
问题#2:现在,我将类型从IQueryable
更改为IEnumerable
。
我在这里的理解是数据库调用是使用where
子句进行的,然后将数据加载到内存中,然后获取第一个元素。
这是真的吗?
public void GetEmployeesByDept(long deptId)
{
IEnumerable<EmployeeDetails> empDetails = _context.EmployeeDetails.Where(x => x.Idseq == deptId);
// First DB call
var firstEmployee = empDetails.Take(1); // in-memory object
Console.WriteLine(empDetails.GetType());
}
如果我的理解不正确,谁能纠正我。
提前致谢
查看查询执行文档
用户创建 LINQ 查询后,该查询将转换为命令树。命令树是与实体框架兼容的查询的表示形式。然后针对数据源执行命令树。在查询执行时,将计算所有查询表达式(即查询的所有组件(,包括结果具体化中使用的那些表达式。
用户创建 LINQ 查询后
您将在以下行中创建查询
IQueryable<EmployeeDetails> empDetails = _context.EmployeeDetails.Where(x => x.Idseq == deptId);
// first DB call
var firstEmployee = empDetails.Take(1);
LINQ 查询始终在循环访问查询变量时执行,而不是在创建查询变量时执行。
因此,当您访问第一个员工变量时,只会进行一次调用。
第二部分说明
IQueryable也是IEnumerable。所以这行:
IEnumerable<EmployeeDetails> empDetails = _context.EmployeeDetails.Where(x => x.Idseq ==deptId);
不强制转换为 IEnumerable。empDetails
保持IQueryable
,并在您访问firstEmployee
变量时再次执行。
仅在需要时通过执行查询来优化 IS。