返回IQueryable<T>和IEnumerable<T>之间的区别是什么,什么时候应该优先于另一个?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

两者都将被延迟执行,何时应该优先于其中一个?


当前回答

上面的答案很好,但它没有提到解释两个接口“如何”不同的表达式树。基本上,有两组相同的LINQ扩展。Where(), Sum(), Count(), FirstOrDefault()等都有两个版本:一个接受函数,一个接受表达式。

IEnumerable版本签名为:Where(Func<Customer, bool> predicate) IQueryable版本签名是:Where(表达式<Func<Customer, bool>>谓词)

你可能在没有意识到的情况下使用了这两个函数,因为它们使用相同的语法调用:

例如,Where(x => x.City == "<City>")对IEnumerable和IQueryable都有效

当在IEnumerable集合上使用Where()时,编译器将一个编译后的函数传递给Where() 当在IQueryable集合上使用Where()时,编译器将表达式树传递给Where()。表达式树类似于反射系统,但用于代码。编译器将代码转换为数据结构,该结构以易于理解的格式描述代码的功能。

为什么要用这个表达式树呢?我只想让Where()过滤我的数据。 主要原因是EF和Linq2SQL orm都可以将表达式树直接转换为SQL,这样您的代码将执行得更快。

哦,这听起来像一个免费的性能提升,我应该在这种情况下使用AsQueryable()吗? 不,IQueryable只有在底层数据提供程序可以使用它时才有用。将常规列表转换为IQueryable不会给你带来任何好处。

其他回答

“IEnumerable”和“IQueryable”之间的主要区别在于在哪里执行筛选逻辑。一个在客户端(内存中)执行,另一个在数据库中执行。

例如,我们可以考虑一个例子,我们的数据库中有一个用户的10,000条记录,假设只有900个是活动用户,所以在这种情况下,如果我们使用“IEnumerable”,那么首先它将所有10,000条记录加载到内存中,然后应用IsActive筛选器,最终返回900个活动用户。

而另一方面,在同样的情况下,如果我们使用“IQueryable”,它将直接应用数据库上的IsActive过滤器,直接从那里将返回900个活跃用户。

上面的答案很好,但它没有提到解释两个接口“如何”不同的表达式树。基本上,有两组相同的LINQ扩展。Where(), Sum(), Count(), FirstOrDefault()等都有两个版本:一个接受函数,一个接受表达式。

IEnumerable版本签名为:Where(Func<Customer, bool> predicate) IQueryable版本签名是:Where(表达式<Func<Customer, bool>>谓词)

你可能在没有意识到的情况下使用了这两个函数,因为它们使用相同的语法调用:

例如,Where(x => x.City == "<City>")对IEnumerable和IQueryable都有效

当在IEnumerable集合上使用Where()时,编译器将一个编译后的函数传递给Where() 当在IQueryable集合上使用Where()时,编译器将表达式树传递给Where()。表达式树类似于反射系统,但用于代码。编译器将代码转换为数据结构,该结构以易于理解的格式描述代码的功能。

为什么要用这个表达式树呢?我只想让Where()过滤我的数据。 主要原因是EF和Linq2SQL orm都可以将表达式树直接转换为SQL,这样您的代码将执行得更快。

哦,这听起来像一个免费的性能提升,我应该在这种情况下使用AsQueryable()吗? 不,IQueryable只有在底层数据提供程序可以使用它时才有用。将常规列表转换为IQueryable不会给你带来任何好处。

我最近遇到了IEnumerable v. IQueryable问题。所使用的算法首先执行IQueryable查询以获得一组结果。然后将这些元素传递给foreach循环,并将这些元素实例化为实体框架(Entity Framework, EF)类。然后在Linq to Entity查询的from子句中使用这个EF类,导致结果为IEnumerable。

我对EF和实体的Linq相当陌生,所以花了一段时间才弄清楚瓶颈是什么。使用MiniProfiling,我找到了查询,然后将所有单独的操作转换为单个IQueryable Linq for Entities查询。IEnumerable执行了15秒,IQueryable执行了0.5秒。其中涉及三个表,在阅读本文后,我相信IEnumerable查询实际上形成了三个表的交叉积并过滤了结果。

尝试使用IQueryables作为经验法则,分析您的工作,使您的更改可测量。

除了前两个非常好的答案(由driis和Jacob):

IEnumerable 接口在系统中。集合名称空间。

IEnumerable对象表示内存中的一组数据,并且只能根据这些数据向前移动。由IEnumerable对象表示的查询立即完整地执行,因此应用程序可以快速地接收到数据。

当执行查询时,IEnumerable加载所有数据,如果我们需要对其进行筛选,筛选本身是在客户端完成的。

可查询接口位于系统中。Linq命名空间。

IQueryable对象提供了对数据库的远程访问,并允许您以从头到尾的直接顺序或反向顺序在数据中导航。在创建查询的过程中,返回的对象是IQueryable,查询是优化的。因此,在执行过程中消耗的内存更少,网络带宽更少,但同时它的处理速度比返回IEnumerable对象的查询稍微慢一些。

选择什么?

如果您需要返回的整个数据集,那么最好使用IEnumerable,它提供了最大的速度。

如果你不需要整个返回的数据集,而只是一些过滤后的数据,那么最好使用IQueryable。

都能让你延期执行,没错。

至于哪个是首选的,这取决于您的底层数据源是什么。

返回一个IEnumerable将自动强制运行时使用LINQ to Objects来查询您的集合。

返回一个IQueryable(顺便说一下,它实现了IEnumerable)提供了额外的功能,可以将您的查询转换为可能在底层源上执行得更好的查询(LINQ to SQL, LINQ to XML,等等)。