简述Linq中.ToList(), .AsEnumerable(), AsQueryable()的区别和用法

标签: blog


这3个方法的功能完全不同, 应按照具体业务场景使用.

AsQueryable()

lazy load 特性

以下是一段最常见的代码,

var products = db.Product.where(p => p.Type == "food").select(p => new { p.Id, p.Name, p.CreateTime});

products 的类型为 IQueryable<T>, 因此不用加 .AsQueryable(). 语句执行后不会立刻查询数据库, 而是在迭代使用 products 时才会查数据库, 具有 lazy load 的特性, 按需查数据库可提高程序效率.

迭代时上面的代码生成的 sql 语句类似:

select Id, Name, CreateTime from Product where Type = 'food'

对 products 再次使用 IQueryable 操作, 编译器会把结果合并为1条 sql 语句, 如下,

var products = db.Product.where(p => p.Type == "food").select(p => new { p.Id, p.Name, p.CreateTime});
var orderedProducts = products.OrderBy(p => p.CreateTime);

迭代时生成的 sql 语句类似:

select Id, Name, CreateTime from Product where Type = 'food' order by CreateTime

显式调用 AsQueryable()?

如果对没有继承 IQueryable 接口的类型使用 AsQueryable(), 会转换类型, 稍微消耗资源, 如下,

int[] array = new { 1, 2, 4, 5};
var enumArray = array.AsQueryable();

因为 Array 只继承了 IEnumerable, 没有继承 IQueryable, 所以会发生类型转换. 当然其中的泛型实现没有拆箱, 对性能影响不大. 但是没有必要, 因为 IQueryable 没有声明任何新方法.

其他有用的情况我暂时还没碰到, 请大家指教.

IQueryable的限制

此外 IQueryable 有诸多限制, 只支持数据库查询语法, 无法支持 Linq to object 的操作, 如以下2段代码会在运行时出错,

var products = db.Product
.Where(p => p.Type == "food")
.Select(p => new { p.Id, p.Name, p.CreateTime.Date});
// 如果改成 .Select(p => new { p.Id, p.Name, DbFunction.TruncateDate(p.CreateTime)})
// 就能正常运行.
var products = db.Product
.Where(p => p.Type == "food")
.Select(p => MyMethod(p));

.Select() 的返回类型为 IQueryable.

第1段代码, 我认为 IQueryable 还不够智能, 无法把 p.CreateTime.Date 转换为 sql 相关的 function, 而使用 DbFunctions 要求使用者了解同时熟悉 linq to entity 及 sql 的内置方法.

第2段代码, 生成 的 sql 无法返回 MyMethod 类型, 是可以理解的.

但是, 对代码加了 AsEnumerable() 后运行正常, 因为 IEnumerable 支持 Linq to object 的操作.

AsEnumerable()

同样支持 lazy load, 但不要滥用

同样是延迟查询, 与 AsQueryable() 区别是, 迭代时遇到 AsEnumerable() 会先进行 sql 查询, 所以对已查出来的结果当然能进行 Linq to object 操作.

但是, 千万不要为了方便而滥用 AsEnumerable(), 可能会严重消耗资源, 如下代码,

var products = db.Product.AsEnumerable()
.Select(p => new {p.Id, p.Name, p.CreateTime.Date});

上面的代码在查询时会把整个Product表的结果存放进内存, 然后进行 .Select 查询!!!

当我不熟悉 DbFunction 或者 要用自定义返回类型应该怎么办

正确的做法应该如下,

var products = db.Product
.Select(p => new {p.Id, p.Name, p.CreateTime})
.AsEnumerable()
.Select(p => MyMethod(p));

AsEnumerable()的限制

如果你写了以下代码,

var products = db.Product
.Select(p => new {p.Id, p.Name, p.CreateTime})
.AsEnumerable()
.Select(p => new {p.Id, p.Name, p.CreateTime.ToString()});
.AsQueryable()
.Where(p=> p.Name == "xxx");

那么最后的 .Where()永远不会执行!!!

因为在使用 AsQueryable().Where() 要求 Linq to entity 进行数据库查询, 但是第一次 AsEnumerable() 时已经进行了数据库查询并且断开连接, 并且查询结果已经作为实实在在的 object, 对 object 不可能再次使用 AsQueryable().Where() 叠加数据库查询!

ToList()

调用 ToList() 会立刻查询并保存结果, 而不会等到迭代时才查询. 作用和 lazy load 是对立的.

在需要得到完整结果后, 再处理的场景, 需要使用 ToList().

例如, 在返回mvc ActionResult 的时候, 要先使用 ToList(), 再作为 model 传给 view. 不知道 mvc 是出于什么考虑不支持在生成 html 的时候lazy load, 请大家指教!

最后

使用 EF 也蛮久了, 确实方便, 但是不熟悉其中原理的话不免要踩坑, 目前正在挣扎从坑里面爬出来lol.

有错的地方请大家指出, 欢迎拍砖.

参考 StackOverflow: What's the difference(s) between .ToList(), .AsEnumerable(), AsQueryable()?

勘误

看到评论指出 .AsEnumerable().AsQueryable().Where(...), Where条件也会生效. 确实文中这一部分描述有误.

实际情况是, 在迭代时遇到AsEnumerable()会先把数据装入内存, 之后调用AsQueryable().Where(), 但这时候底层的类型是IEmumerable而不是IQueryable, 所以框架会使用IEnumeralbe.Where(), 因此能够过滤数据. 然而, 当继续使用.Include(), .Where(i => DbFunctions...)IQueryable独有的方法时, 框架会使用空实现, 没有效果.

简述Linq中.ToList(), .AsEnumerable(), AsQueryable()的区别和用法的更多相关文章

  1. Linq中join & group join & left join 的用法

    Linq中join & group join & left join 的用法 2013-01-30 11:12 12154人阅读 评论(0) 收藏 举报  分类: C#(14)  文章 ...

  2. 图论中DFS与BFS的区别、用法、详解…

    DFS与BFS的区别.用法.详解? 写在最前的三点: 1.所谓图的遍历就是按照某种次序访问图的每一顶点一次仅且一次. 2.实现bfs和dfs都需要解决的一个问题就是如何存储图.一般有两种方法:邻接矩阵 ...

  3. 图论中DFS与BFS的区别、用法、详解?

    DFS与BFS的区别.用法.详解? 写在最前的三点: 1.所谓图的遍历就是按照某种次序访问图的每一顶点一次仅且一次. 2.实现bfs和dfs都需要解决的一个问题就是如何存储图.一般有两种方法:邻接矩阵 ...

  4. 简述java中equals()方法和==的区别

    ==与equals的主要区别是: ==: ==常用于比较原生类型(基本数据类型):byte,short,char,int,long,float,double,boolean,比较的是他们的值. 若用= ...

  5. 简述jq中attr()和prop()的区别

    attr,prop都是属性的意思,那他们有什么区别呢?我们先来看一下jquery的部分源码: attr部分: attr: function( elem, name, value, pass ) { v ...

  6. 简述vue中v-if和v-show的区别

    vue中的 v-if 和 v-show 二者都可以动态的控制元素的隐藏和显示,但是他们控制的原理是不同的 v-if v-if 控制元素显示或隐藏是把dom元素整个的渲染或者删除,如果删除,也就是页面中 ...

  7. Javascript中call和apply的区别和用法

    JavaScript中有一个call和apply方法,其作用基本相同,但也有略微的区别.其实就是更改对象的内部指针,即改变对象的this指向的内容.这在面向对象的js编程过程中有时是很有用的.call ...

  8. CSS中的class与id区别及用法

    转自http://www.divcss5.com/rumen/r3.shtml及http://www.jb51.net/css/35927.html 我们平常在用DIV CSS制作Xhtml网页页面时 ...

  9. HTML中padding和margin的区别和用法

     margin(外边距) 定义:margin是用来隔开元素与元素的间距,发生在元素本身的外部,margin用于布局分开元素使元素与元素互不相干. 提示:margin: top right bottom ...

随机推荐

  1. hdu 5620 KK's Steel(推理)

    Problem Description Our lovely KK has a difficult mathematical problem:he has a N(1≤N≤1018) meters s ...

  2. Object-c 单例模式中的 allocWithZone作用

    最 近因为在ios应用开发中,考虑到一些公共方法的封装使用,就决定使用单例模式的写法了..不知道,Object-c中的单例模式的写法是否和java中的写法是否有所区别? 于是阿堂从网上一搜,发现“ O ...

  3. ASP.NET之自定义异步HTTP处理程序(图文教程)

    前面我们学习了关于关于自定义同步HTTP处理程序,相信大家可能感觉有所成就,但是这种同步的机制只能对付客户访问较少的情况或者数据处理量不大的情况,而今天这篇文章就是解决同步HTTP处理程序的这个致命缺 ...

  4. 百度地图 Android SDK - Hello Baidu Map

    例如,给广大以下主要开发者介绍了如何使用百度地图Android SDK构造的主应用程序的地图! 第一步.创建Androidproject,将百度地图Android SDK的开发包导入到project对 ...

  5. 性能强悍的开源关系数据库PostgreSQL

    性能强悍的开源关系数据库PostgreSQL

  6. 《think in python》学习-10

    think in python 10 列表 和字符串相似,列表是值得序列.在列表中,它可以是任何类型,列表中的值成为元素,有时也称为列表项 s = [10,20,30,40] print s #列表也 ...

  7. MVC 5 + EF 6

    (一) ??运算符 C#中两个问号(“?”)的作用是判断“?”左边的对象是否为null,如果不为null则使用“?”左边的对象,如果为null则使用“?”右边的对象. (二)VS安装Entity Fr ...

  8. WindowsForm 记事本 对话框

    textbox:     属性:         text:文本         selectedtext:获取或设置选中文本         canundo:是否能够撤销     方法:       ...

  9. 在Linux上使用cmake创建CodeBlocks工程

    最近在linux上使用cmake,对于使用GUI习惯的还真不能适应,真是想尽一切办法把原来使用cmake的工程创建成CodeBlocks工程.工程小了还能接受,工程大了太麻烦了. 看了一下cmake的 ...

  10. MVC 创建带图片的<A></A>标签

    <a href="@Url.Action("Detail", "Product", new { messageId = item.message ...