共有两组 LINQ 标准查询运算符,一组在类型为 IEnumerable<T> 的对象上运行,另一组在类型为 IQueryable<T>的对象上运行。构成每组运算符的方法分别是 EnumerableQueryable 类的静态成员。这些方法被定义为作为方法运行目标的类型的“扩展方法”。这意味着可以使用静态方法语法或实例方法语法来调用它们。

大家应该还记得,上节我们说过linq查询要执行在clr上是把查询语句变成扩展方法来执行,这两套东西不仅返回类型不同连所定义的扩展方法都不同,所以 我们完全可以把这2套东西理解成完全不同的东西,那么,什么时候用IEnumerable<T> , 什么时候用IQueryable<T> 呢? 让我们再来看看msdn的介绍:

对于在内存中集合上运行的方法(即扩展 IEnumerable<T>
的那些方法),返回的可枚举对象将捕获传递到方法的参数。在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。

与之相反,扩展 IQueryable <T> 的方法不会实现任何查询行为,但会生成一个表示要执行的查询的表达式树。查询处理由源
IQueryable<T> 对象处理。

一言以蔽之,本地数据源用IEnumerable<T> , 并且查询的逻辑可以直接用你所定义的方法的逻辑(因为有上下文),远程数据源用IQueryable ,无法直接使用你所定义的方法的逻辑,必须先生成表达式树,查询由源对象处理。

下面我们再来看一个例子来证明这是两套完全不同的东西:

首先是本地数据源:

List<string> names = new List<string> { "Cai", "Wxied", "Beauty" };

然后我们看看names的where方法

VS的智能提示会告诉我们(sorry,这个地方实在不好截图,大家可以自 己尝试,我先给大家描述下)这个扩展方法有2个重载,必须传入Func<T>,返回IEnumerable<T>

再提一点知识,Func<T>叫谓语表达式,相当于一个委托, 我认为 ,之所以可以直接传Func<T>是因为本地数据源可以直接执行方法的逻辑。

再让我们来看看一个远程数据源:

DataClasses1DataContext dataContext = new DataClasses1DataContext();

dataContext.Customers.Where这个方法有4个重载。必须传入 Expression<Func<T>>,返回IQueryable<T>

大家和上面对比一下,就会发现本地数据源和远程数据源的扩展方法完全不一样,而且远程数据源不能直接传Func<T>,必须用一个 Expression来包装这个Func<T>,正好又从一个方面验证了我们之前所提到的知识。

总结之,IEnumerable<T>查询必须在本地执行.并且执行查询前我们必须把所有的数据加载到本地.而且更多的时候.加载的数据有大量的数据是我们不需要的无效数据.但是我们却不得不传输更多的数据.做更多的无用功.而IQueryable<T>却总能只提供你所需要的数据.大大减少了数据的传输.这就好比我们在小作坊下订单.小作坊老板跟我们说.他的货刚好比你所需要的多出一些.你只能要了它.不然剩下一点他不好卖等等.而大工厂却不会出现这种状况.


深入理解IEnumerable和IQueryable两接口的区别

无论是在ado.net EF或者是在其他的Linq使用中,我们经常会碰到两个重要的静态类EnumerableQueryable,他们在System.Linq命名空间下。那么这两个类是如何定义的,又是来做什么用的呢?特别是Queryable类,它和EF的延迟加载技术有什么联系呢?

好,带着上面的问题开始我们今天的学习。

首先介绍两个类的定义

(1)Enumerable类,对继承了IEnumerable<T>接口的集合进行扩展;

(2)Queryable类,针对继承了IQueryable<T>接口的集合进行扩展。

在继续学习之前,我们先来看一下EF中定义的实体集DbSet<T>

通过上面的截图我们可以看到 DbSet<T>实现了IQueryable<T>IEnumerable<T>接口。

与上面的两句话结合起来意思就是可以通过两个静态类对DbSet<T>进行扩展操作。其实查看两个类的源码可以知道,这两个类对实现了IQueryable<T>IEnumerable<T>接口的集合进行了很多方法的扩展。

可能你还不知道如何进行扩展方法的定义以及操作,没事儿,请参考另外一篇文章:C#扩展方法的理解

但是那么的扩展方法不都是我们需要的,我们在ado.net EF中最常用的就是扩展的Where方法。

两个类中Where扩展方法的定义分别如下

(1)Enumerable类

    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);  public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);  

观察Where方法,可以看到第一个参数是实现了IEnumable接口的类,第二个参数是一个Func<T>委托类型

(2)Queryable类

    public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);  public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, int, bool>> predicate);  

观察Where方法,可以看到第一个参数是实现了IEnumable接口的类,第二个参数是一个Expresssion类型
很显然,两个类扩展的Where方法是不同的,那具体有什么不同呢?那么这种不同又导致什么结果呢?

OK,带着疑问继续往下学习。

为了便于大家好学习,在这里,我们编写一段代码,通过监视工具查看两者的区别。

先上代码

    private void Form1_Load(object sender, EventArgs e)  {  using (DemoContext context = new DemoContext())  {  var customer = context.cunstomer.Where(c => c.Name == "牡丹");  foreach (var item in customer)  {  MessageBox.Show(item.Id.ToString());  }  }  }  

至于代码中的上下文定义以及实体集大家不必纠结,我们在这里要透过表象看本质。在上面的程序中添加断点,同时启动sql server profiler监视工具,运行程序。

程序会在断点处停下来,如下所示

上面只是到点断点处,当然断点处的语句还有执行,继续单步执行。

执行过断点处所在的语句,观察监视工具还是什么都没有。

咦,是不是出什么问题了呢?为什么没有查询语句执行呢?真的是监视工具出问题了吗?

继续单步调试

咦,这个时候怎么出现sql查询语句了。很奇怪吧,这就是ado.net EF的延迟加载技术,这里面很重要的一部分就是通过IQueryable接口实现的(具体我们放到最后再说)。

讲过了Queryable类的Where方法,接下来我们再来看一下Enumable类的Where方法。

修改上面的代码如下所示

    private void Form1_Load(object sender, EventArgs e)  {  using (DemoContext context = new DemoContext())  {  var customer = context.cunstomer.Where(c => c.Name == "牡丹").AsEnumerable();  foreach (var item in customer)  {  MessageBox.Show(item.Id.ToString());  }  }  }  

同样是打开监视工具,添加断点,运行程序

单步调试,继续运行

执行过断点所在的语句及执行了查询语句。

关于上面的两个测试总结如下。

(1)所有对于IEnumerable的过滤,排序等操作,都是在内存中发生的。也就是说数据已经从数据库中获取到了内存中,只是在内存中进行过滤和排序操作。

(2)所有对于IQueryable的过滤,排序等操作,只有在数据真正用到的时候才会到数据库中查询。这也是Linq的延迟加载核心所在。

那最后一个问题,IQueryable接口为何那么特殊呢?

观察它的定义

    // 摘要:  //     提供对未指定数据类型的特定数据源的查询进行计算的功能。  public interface IQueryable : IEnumerable  {  // 摘要:  //     获取在执行与 System.Linq.IQueryable 的此实例关联的表达式树时返回的元素的类型。  //  // 返回结果:  //     一个 System.Type,表示在执行与之关联的表达式树时返回的元素的类型。  Type ElementType { get; }  //  // 摘要:  //     获取与 System.Linq.IQueryable 的实例关联的表达式树。  //  // 返回结果:  //     与 System.Linq.IQueryable 的此实例关联的 System.Linq.Expressions.Expression。  Expression Expression { get; }  //  // 摘要:  //     获取与此数据源关联的查询提供程序。  //  // 返回结果:  //     与此数据源关联的 System.Linq.IQueryProvider。  IQueryProvider Provider { get; }  }  

该接口有三个特殊的属性,具体内容代码已经介绍了,那查询时具体又是如何执行呢?
答案是该接口会把查询表达式先缓存到表达式树中,只有当真正遍历发生的时候,才会由IQueryProvider解析表达式树,生成sql语句执行数据库查询操作。

哎呀,写到现在终于差不多快写完了。

上面介绍了两个接口的区别与联系,具体使用哪种就看自己的项目需求了。

最后补充一下List.Where()方法,还是以代码说明。

List<string> fruits = new List<string> { "apple", "passionfruit", "banana", "mango","orange", "blueberry", "grape", "strawberry" };IEnumerable<string> query = fruits.Where(fruit => fruit.Length < 6);foreach (string fruit in query)
{Console.WriteLine(fruit);
}

查看List<T>的定义,如下图所示

它也是继承了IEnumerable接口,因此,他也不存在延迟加载。
OK,到此,所有工作完成。

IEnumerable与IQueryable相关推荐

  1. IEnumerable和IQueryable在使用时的区别

    最近在调研数据库查询时因使用IEnumerable进行Linq to entity的操作,造成数据库访问缓慢.此文讲述的便是IEnumerable和IQueryable的区别. 微软对IEnumera ...

  2. Entity Framework返回IEnumerable还是IQueryable?

    在使用EF的过程中,我们常常使用repository模式,本文就在repository层的返回值是IEnumerable类型还是IQueryable进行探讨. 阅读目录: 一.什么是Repositor ...

  3. 一个简单问题引发对IEnumerable和IQueryable的思考

    问题概述:    首先看下图,有客户表和客户负责人表关系是多对多,访问数据库使用的是EF所以这里我们开启了延迟加载,需求就是将每个客户的所有负责人逗号拼接显示在负责人这一栏位, 对你没看错需求就是这么 ...

  4. IEnumerable和IQueryable的区别以及背后的ExpressionTree表达式树

    关于IEnumerable和IQueryable的区别,这事还要从泛型委托Func<T>说起.来看一个简单的泛型委托例子: class Program { static void Main ...

  5. EntityFramework中IEnumerable和IQueryable的含义和区别

    先说下IList,IList对SQL语句是即时执行的,IEnumerable和IQueryable是延时执行的,用到才执行. IQueryable和IEnumerable在每次执行时都必须连接数据库读 ...

  6. 【温故知新】C#中 IEnumerable 与IQueryable

    微信公众号:趣编程ACE 关注可了解更多的.NET日常实战开发技巧,如需源码 后台回复 源码 即可; 如果觉得对你有帮助,欢迎关注 老生常谈 C#中 IEnumerable 与IQueryable I ...

  7. EF Core:基于关系的复杂查询 区分IEnumerable和IQueryable

    基于关系的复杂查询 表结构描述:一个文章带有多个评论,现在想输出所有,评论中带"微软"的文章.换句话说开,只要这个文章中的评论带这个词,就放入集合中去. 基于EF Core的实体间 ...

  8. IEnumerable和IQueryable的区别

    IEnumerable<T>在.Net2.0中我们已经很熟悉了.你想要利用Foreach迭代吗?实现 IEnumerable<T>吧!你想直接做为数据源绑定到控件吗?使用IEn ...

  9. 最全数据结构详述: List VS IEnumerable VS IQueryable VS ICollection VS IDictionary

    本文对常用的数据结构详述:Array, ArrayList,List,IList,ICollection, Stack, Queue, HashTable, Dictionary, IQueryabl ...

最新文章

  1. php curl p12证书处理,PHP如何支持CURL字符串证书传输(详细解决过程)
  2. 牛客题霸 [找到字符串的最长无重复字符子串] C++题解/答案
  3. 选数(洛谷P1036题题解,Java语言描述)
  4. web前端技巧:如何在浏览器地址栏执行html代码
  5. stream对多个字段分组_java8 stream 如何按多字段分组,并对一个字段求和
  6. 苹果确认部分AirPods Pro存在静电噪音等声音问题 将免费更换
  7. java 测试磁盘io,详解三种Linux测试磁盘IO性能的方法总结,值得收藏
  8. A/B test模块使用以及配置
  9. iOS开发常用的RGB色值
  10. python中基例_python | 自定义函数
  11. Linux系统中僵尸进程的查看与删除
  12. DAO 的具体内涵与概念
  13. 拼多多通用优惠券漏洞被薅羊毛数千万 你的系统有反作弊防护吗?
  14. LeetCode 香槟塔
  15. A fastandrobust convolutionalneuralnetwork-based defect detection model inproductqualitycontrol-阅读笔记
  16. 伊诺ET-33夹式校(音器吉他/贝司/小提琴/尤克里里 使用
  17. linux环境安装部署RF+Jenkins+Git(非完整版)
  18. 我们需要培养职业化的工作习惯
  19. 堡垒机和防火墙的区别是什么?能防删库跑路吗?
  20. Educational Codeforces Round 88 (Rated for Div. 2)C. Mixing Water(数学+二分法)---题解

热门文章

  1. android开发案例
  2. JAVA数组拼接 join_在 Java 中如何使用 join 方法连接多个字符串?
  3. 一文理解http缓存机制
  4. arcgis for js图层graphic的鼠标悬浮事件
  5. ryzen7能否安装linux,AMD 锐龙(Ryzen)7 装win7系统及bios设置教程(完美支持)
  6. Python解压zip文件出现TypeError: pwd: expected bytes, got str的解决方案
  7. 2009年5月22日星期五
  8. 大型数据中心浸没式液冷与风冷投资成本分析
  9. python解析yml配置文件
  10. 高考报志愿选什么专业?