IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,而IQueryable<T> 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令。也是延迟在我要真正显示数据的时候才执行。
LINQ查询一共提供了两类扩展方法,在System.Linq命名空间下,有两个静态类:Enumerable类,它针对继承了IEnumerable<T>接口的集合进行扩展;Queryable类,它针对继承了IQueryable<T>接口的集合类进行扩扎。接口IQueryable<T>也是继承了IEnumerable<T>接口的,所以,致使两个接口的方法在很大程度上时一致的。
LINQ查询从功能上来讲实际上可分为3类: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语句,然后在数据库中执行。
简单的说:本地数据源用IEnumerable<T>,远程数据源用IQueryable<T>。
1.Func<>谓词表达式,就是一个委托,委托一旦调用,就立即执行了,将执行结果保存在内存中。
2.Expression是一个表达式,会存储拼接表达式树,直到在运行期最终执行。
那么在EF中我们根据条件查询数据时,不应该把数据一次性加载到本地内存中,然后再本地内存中进行筛选,如果数据量大了,就崩溃了。
我们需要将表达式组合好,然后再一起提交到数据库执行,返回查询结果。
(每次在执行where查询操作符的时候IQueryProvider会为我们创建一个新的IQueryable<T>,调用AsEnumerable()方法的时候并不会去实际取值,只是
得到了一个IEnumerable,所以EF在查询数据时候不要先取IEnumerable再去筛选数据。执行ToList方法时才会去真正调用迭代器GetEnumerator()
取值。真正取值时候,会去执行IQueryProvider中的Excute方法.(解析表达式,然后执行取得结果))
这就是IQueryable的延迟加载