.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&lt;T&gt;。但是,一旦将其转换为 IEnumerable&lt;T&gt;,编译器必须选择 IEnumerable&lt;T&gt; 扩展方法。

【讨论】:

是的.... 现在我都清楚了。我没有想到我们可以在接口 IEnumerable 和 IQueryable 上使用扩展方法。我只考虑接口实现,想知道这一切是如何在运行时发生的。甚至没有考虑过这个编译时场景。非常感谢。【参考方案2】:

您没有调用相同的 .Take() 方法。

Enumerable.Take 返回一个新的IEnumerable,它将枚举枚举中的项目并生成它们。

Queryable.Take 返回一个新的IQuerable,以表示Expression 树。当您开始枚举该可查询对象时,表达式将被编译成 sql。

【讨论】:

以上是关于.Take() 的行为如何根据我在左侧使用的接口参考发生变化。 IQueryable 与 IEnumerable的主要内容,如果未能解决你的问题,请参考以下文章

springBoot接口排队(串行执行)

休眠。 QueryOver Take(n) - 使用 Left.Join

如何使用 Agent 的参数来设置 Block 的行为

django rest framework接口怎么传参进行数据查询?

pytorch-(torch.take())根据索引返回指定索引上的数据集合

BUGKU (Take the maze)