购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

建议29:区别LINQ查询中的IEnumerable<T>和IQueryable<T>

LINQ查询方法一共提供了两类扩展方法,在System.Linq命名空间下,有两个静态类:Enumerable类,它针对继承了IEnumerable<T>接口的集合类进行扩展;Queryable类,它针对继承了IQueryable<T>接口的集合类进行扩展。稍加观察我们会发现,接口IQueryable<T>实际也是继承了IEnumerable<T>接口的,所以,致使这两个接口的方法在很大程度上是一致的。那么,微软为什么要设计出两套扩展方法呢?

我们知道,LINQ查询从功能上来讲实际上可分为三类:LINQ to OBJECTS、LINQ to SQL、LINQ to XML(本建议不讨论)。设计两套接口的原因正是为了区别对待LINQ to OBJECTS、LINQ to SQL,两者对于查询的处理在内部使用的是完全不同的机制。针对LINQ to OBJECTS时,使用Enumerable中的扩展方法对本地集合进行排序和查询等操作,查询参数接受的是Func<>。Func<>叫做谓语表达式,相当于一个委托。针对LINQ to SQL时,则使用Queryable中的扩展方法,它接受的参数是Expression<>。Expression<>用于包装Func<>。LINQ to SQL引擎最终会将表达式树转化成为相应的SQL语句,然后在数据库中执行。

那么,到底什么时候使用IQueryable<T>,什么时候使用IEnumerable<T>呢?简单表述就是:本地数据源用IEnumerable<T>,远程数据源用IQueryable<T>。要体会这两者之间的区别,我们来分析下面的代码:


private static void NewMethod3()

{

DataContext ctx=new DataContext("server=192.168.0.102;database=Temp;

uid=sa;pwd=sa123");

Table<Person>persons=ctx.GetTable<Person>();

var temp1=(from p in persons where p.Age>20 select p)

.AsEnumerable<Person>();

var temp2=from p in temp1 where p.Name.IndexOf('e')>0 select p;

foreach(var item in temp2)

{

Console.WriteLine(string.Format("Name:{0}\tAge:{1}",item.Name,

item.Age));

}

}

private static void NewMethod2()

{

DataContext ctx=new DataContext("server=192.168.0.102;database=Temp;

uid=sa;pwd=sa123");

Table<Person>persons=ctx.GetTable<Person>();

var temp1=from p in persons where p.Age>20 select p;

var temp2=from p in temp1 where p.Name.IndexOf('e')>0 select p;

foreach(var item in temp2)

{

Console.WriteLine(string.Format("Name:{0}\tAge:{1}",item.Name,

item.Age));

}

}


在Method3中,虽然针对temp1使用的也是延迟求值,但是在整个LINQ查询语句的最后对结果使用了AsEnumerable方法,这相当于将远程数组转成了本地数据。通过数据库的监视工具也验证了这一点,我们查看到最终Method3中执行的SQL语句是:


exec sp_executesql N'SELECT[t0].[Name],[t0].[Age]

FROM[Person]AS[t0]

WHERE[t0].[Age]>@p0',N'@p0 int',@p0=20


以上语句针对建议28中提到的数据库表Person中的存储内容,将会返回两条记录。

对于Method2方法,我们在建议28中曾提到,它所生成的SQL语句是组合了Age及Name筛选条件的,所以最终仅仅返回符合条件的记录。如果不理解这一点,可能会在写程序的时候造成性能的损耗。所以,在LINQ to SQL的查询中,要尽量始终使用IQueryable<T>。

在使用IQueryable<T>和IEnumerable<T>的时候还需要注意一点,IEnumerable<T>查询的逻辑可以直接用我们自己所定义的方法,而IQueryable<T>则不能使用自定义的方法,它必须先生成表达式树,查询由LINQ to SQL引擎处理。在使用IQueryable<T>查询的时候,如果使用自定义的方法,则会抛出异常。


DataContext ctx=new DataContext("server=192.168.0.102;database=Temp;

uid=sa;pwd=sa123");

Table<Person>persons=ctx.GetTable<Person>();

var temp1=from p in persons where OlderThan20(p.Age)select p;

foreach(var item in temp1)

{

Console.WriteLine(string.Format("Name:{0}\tAge:{1}",item.Name,

item.Age));

}


以上代码抛出异常NotSupportedException:

方法"Boolean OlderThan20(Int32)"不支持转换为SQL。

但是,如果我们将查询换成一个IEnumerable<T>查询,这种模式是支持的:


List<int>list=new List<int>(){19,20,21,22};

var temp2=from c in list where OlderThan20(c)select c; L8krN47qkRTFAPqZMRhpCpwlHmBFCA/920Bv+OolFjGItmAAEBA5aAmO0h9BGTav


点击中间区域
呼出菜单
上一章
目录
下一章
×

打开