.Take() 的行为如何根据我在左侧使用的接口参考发生变化。 IQueryable 与 IEnumerable
Posted
技术标签:
【中文标题】.Take() 的行为如何根据我在左侧使用的接口参考发生变化。 IQueryable 与 IEnumerable【英文标题】:How does the behavior of .Take() changes based on the interface reference I'm using on left. IQueryable vs IEnumerable 【发布时间】:2021-10-03 18:33:54 【问题描述】:假设我有这些示例代码
IQueryable<Employee> data = context.Employees.Where(x => x.FullName != "Shekar Reddy");
var topEmp = data.Take(1);
foreach (var item in topEmp)
Console.WriteLine(item.FullName);
和
IEnumerable<Employee> data = context.Employees.Where(x => x.FullName != "Shekar Reddy");
var topEmp = data.Take(1);
foreach (var item in topEmp)
Console.WriteLine(item.FullName);
它们之间的唯一区别是我使用 IQueryable 与 IEnumerable 的引用。 第一个 sn-p 正在生成 sql 查询以获取与过滤条件匹配的前 1 个项目,而后者正在生成没有顶部过滤器的 sql 查询。
在这两种情况下,data 中的对象都是类型 Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 但是第一个场景中的 topEmp 是类型:Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 和 其次是 System.Linq.Enumerable.EnumerablePartition
.Take 方法的行为如何根据左侧的接口引用发生变化。我看到“数据”变量在两种情况下都属于同一类型。根据我的理解,如果左侧引用是一个类,那么 .Take of that class 可能会被调用;但它们是接口。我确定我在这里缺少 C# 的基本概念。
【问题讨论】:
【参考方案1】:你是正确的,你错过了 C# 的一个基本概念,这个概念是扩展方法。
我们没有调用由Enumerable
对象实现的抽象Take
方法。
相反,我们正在调用与对象分开存在的函数:Take
扩展方法。根据对象的类型有不同的方法。
在 System.Core 库中存在这些静态类。
public static class Queryable
public static IQueryable<TSource> Take<TSource>(
this IQueryable<TSource> source,
int count
);
// Many other methods...
public static class Enumerable
public static IEnumerable<TSource> Take<TSource>(
this IEnumerable<TSource> source,
int count
);
// Many other methods...
扩展方法在编译时解析,而不是在运行时解析。
您的data
变量恰好实现了IQueryable<T>
。但是,一旦将其转换为 IEnumerable<T>
,编译器必须选择 IEnumerable<T>
扩展方法。
【讨论】:
是的.... 现在我都清楚了。我没有想到我们可以在接口 IEnumerable您没有调用相同的 .Take()
方法。
Enumerable.Take
返回一个新的IEnumerable
,它将枚举枚举中的项目并生成它们。
Queryable.Take
返回一个新的IQuerable
,以表示Expression
树。当您开始枚举该可查询对象时,表达式将被编译成 sql。
【讨论】:
以上是关于.Take() 的行为如何根据我在左侧使用的接口参考发生变化。 IQueryable 与 IEnumerable的主要内容,如果未能解决你的问题,请参考以下文章
休眠。 QueryOver Take(n) - 使用 Left.Join
django rest framework接口怎么传参进行数据查询?