目录html
标签: blogsql
这3个方法的功能彻底不一样, 应按照具体业务场景使用.数据库
如下是一段最多见的代码,mvc
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 语句, 以下,code
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 语句相似:htm
select Id, Name, CreateTime from Product where Type = 'food' order by CreateTime
若是对没有继承 IQueryable 接口的类型使用 AsQueryable(), 会转换类型, 稍微消耗资源, 以下,blog
int[] array = new { 1, 2, 4, 5}; var enumArray = array.AsQueryable();
由于 Array 只继承了 IEnumerable, 没有继承 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 的操做.
一样是延迟查询, 与 AsQueryable() 区别是, 迭代时遇到 AsEnumerable() 会先进行 sql 查询, 因此对已查出来的结果固然能进行 Linq to object 操做.
可是, 千万不要为了方便而滥用 AsEnumerable(), 可能会严重消耗资源, 以下代码,
var products = db.Product.AsEnumerable() .Select(p => new {p.Id, p.Name, p.CreateTime.Date});
上面的代码在查询时会把整个Product表的结果存放进内存, 而后进行 .Select 查询!!!
正确的作法应该以下,
var products = db.Product .Select(p => new {p.Id, p.Name, p.CreateTime}) .AsEnumerable() .Select(p => MyMethod(p));
若是你写了如下代码,
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() 会马上查询并保存结果, 而不会等到迭代时才查询. 做用和 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
独有的方法时, 框架会使用空实现, 没有效果.