自己的理解:linq中AsEnumerable和AsQueryable的区别

  1. IEnumerable内存对象

IQuerable表达树式,没有使用的情况下,还不会向数据库请求连接取数据

 

  2.linqToSQL和LinqToEntity中默认返回的是IQuerable 

  参考:http://www.cnblogs.com/ambon/articles/4766924.html

 IQueryable<T_Class> classesIQue = (from c in schoolEntities.T_Class
orderby c.ID
select c).Skip<T_Class>(3).Take<T_Class>(3);
可以用AsEnumerable
            IEnumerable<T_Class> classesIEnu = (from c in schoolEntities.T_Class
orderby c.ID
select c).AsEnumerable<T_Class>

 

  3. 我看的NerdDinner文档上这么说的,跟你的例子一样,上面大致的意思:IQueryable<T>类型可以在最终的执行前加一些方法,然后再返回最终的  IQueryable<T>;而IEnumerable<T>已经包含了你执行查询后的所有结果,这时你的添加方法都是在已经有的结果集里进行。

 

  4. First()、count()等操作不是延迟加载是立即执行的,怎么看呢?就看源代码中的返回类型,如果是IEnumerable<T>的或者IEnumerable的就是延迟加载的,像count是int类型的就不是。

 

  5.关于IEnumerable<T>,数据库查询出的数据用IEnumerable<T>很理解,但我有时候一个小用途,或者说实体类里聚合的另一对象集,仅做增删改查用,也要定义成IEnumerable<T>,而我一般用个List<T>就OK了

 

这样数据机构改的话,函数定义不需要改。因此函数定义中的类型,尽量使用继承树上越上面的类/接口越好。

 

   7. 使用IEnumerable<T>,可以充分利用Linq的延迟执行特性,降低计算量。不理解的话看下这段代码:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Collections.Generic;
using System.Linq;
 
class Program
{
 
    static void Main(string[] args)
    {
        var nums = GetAllNumber();
        //var nums = GetAllNumber().ToList();
        var t = nums.Skip(100000).Take(2000).Where(x => x % 7 == 0);
        foreach(int in t)
        {
            Console.Write("{0}\t", i.ToString());
        }
    }
     
    static IEnumerable<int> GetAllNumber()
    {
        for (int i = 0;i<int.MaxValue;i++)
        {
            yield return i;
        }
        yield return int.MaxValue;
    }
}


考虑下取消注释行并注释上一行是什么结果。

 

 

 

 

 

 

 大家都知道可以使用IEnumerable<T> 的AddRange方法去获取指定范围的数据(常用于分页)。昨天我在做分页时当我取下来的数据时发现速度会很慢而且内存也消耗比较多,由此我们可以猜想到加载数据的时候肯定加载了很多不需要的数据。

代码:


 public PageList(IEnumerable<T> source, int index, int pageSize,int totalCount)
        {
            this.AddRange(source.Skip(index*pageSize).Take(pageSize));
            this.TotalCount = source.Count();
            this.PageSize=pageSize;
            this.PageIndex=index;
           
            this.TotalPageCount = (int)Math.Ceiling(TotalCount / (double)PageSize);
        }

 

用SQL Server profiler 工具查看一下SQL 语句时果然IEnumerable<T>把所有的数据加载下来了再对其进行选择其中的数据,sql代码如下:

复制代码
SELECT 
[Extent1].[FID] AS [FID], 
[Extent1].[BookName] AS [BookName], 
[Extent1].[Author] AS [Author]
FROM ( SELECT [Extent1].[FID] AS [FID], [Extent1].[BookName] AS [BookName], [Extent1].[Author] AS [Author], row_number() OVER (ORDER BY [Extent1].[FID] ASC) AS 
[row_number]
    FROM [dbo].[Book] AS [Extent1]
)  AS [Extent1]
WHERE [Extent1].[row_number] > 0

ORDER BY [Extent1].[FID] ASC

复制代码

由些可见IEnumerable生成的SQL语句是查询所有的数据,而不是生成查询指定记录的SQL语句。在上面PageList 的构造函数中 使用IEnumerable<T> source 参数,当我把IEnumerable<T> 把换成IQueryable<T>后再执行时发现速度明显提高了,再有SQL Server profiler 工具查看一下SQL代码:

复制代码
SELECT TOP (10) 
[Extent1].[FID] AS [FID], 
[Extent1].[BookName] AS [BookName], 
[Extent1].[Author] AS [Author]
FROM ( SELECT [Extent1].[FID] AS [FID], [Extent1].[BookName] AS [BookName], [Extent1].[Author] AS [Author], row_number() OVER (ORDER BY [Extent1].[FID] ASC) AS 
[row_number]
    FROM [dbo].[Book] AS [Extent1]
)  AS [Extent1]
WHERE [Extent1].[row_number] > 0
ORDER BY [Extent1].[FID] ASC
复制代码

 显然,这个才是我们真正需要的(因为我设置的是每页10条数据)--只查询我们指定数据。

思索结果:

  原来是IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,而IQueryable<T> 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令。也是延迟在我要真正显示数据的时候才执行。

以上只是我的个人想法,也不知道是否正确,不到之还望指点,欢迎大家拍砖。 如果有牛人路过实属万幸,希望能指点一二。 

posted @ 2016-10-27 17:41  阿玛  阅读(563)  评论(0)    收藏  举报