返回 IEnumerable与 IQueryable 比较

发布于 2024-09-02 16:37:38 字数 356 浏览 4 评论 0 原文

返回 IQueryableIEnumerable 之间有什么区别,什么时候应该优先选择其中一个?

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;

两者都会延迟执行吗?什么时候应该优先选择其中一个?

What is the difference between returning IQueryable<T> vs. IEnumerable<T>, when should one be preferred over the other?

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;

Will both be deferred execution and when should one be preferred over the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

橘味果▽酱 2024-09-09 16:38:23

除了上述内容之外,值得注意的是,如果使用 IQueryable 而不是 IEnumerable,则可能会出现异常:

如果 products 则以下内容可以正常工作是一个 IEnumerable

products.Skip(-4);

但是,如果 products 是一个 IQueryable 并且它尝试访问数据库表中的记录,那么您将收到此错误:

OFFSET 子句中指定的偏移量不能为负。

这是因为构造了以下查询:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

并且 OFFSET 不能有负值。

In addition to the above, it's interesting to note that you can get exceptions if you use IQueryable instead of IEnumerable:

The following works fine if products is an IEnumerable:

products.Skip(-4);

However if products is an IQueryable and it's trying to access records from a DB table, then you'll get this error:

The offset specified in a OFFSET clause may not be negative.

This is because the following query was constructed:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

and OFFSET can't have a negative value.

眼角的笑意。 2024-09-09 16:38:21

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

IE可枚举
接口位于 System.Collections 命名空间中。

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

当执行查询时,IEnumerable会加载所有数据,如果我们需要过滤它,过滤本身是在客户端完成的。

IQueryable 接口位于 System.Linq 命名空间中。

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

选择什么?

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

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

In addition to first 2 really good answers (by driis & by Jacob) :

IEnumerable
interface is in the System.Collections namespace.

The IEnumerable object represents a set of data in memory and can move on this data only forward. The query represented by the IEnumerable object is executed immediately and completely, so the application receives data quickly.

When the query is executed, IEnumerable loads all the data, and if we need to filter it, the filtering itself is done on the client side.

IQueryable interface is located in the System.Linq namespace.

The IQueryable object provides remote access to the database and allows you to navigate through the data either in a direct order from beginning to end, or in the reverse order. In the process of creating a query, the returned object is IQueryable, the query is optimized. As a result, less memory is consumed during its execution, less network bandwidth, but at the same time it can be processed slightly more slowly than a query that returns an IEnumerable object.

What to choose?

If you need the entire set of returned data, then it's better to use IEnumerable, which provides the maximum speed.

If you DO NOT need the entire set of returned data, but only some filtered data, then it's better to use IQueryable.

世界等同你 2024-09-09 16:38:18

我们可以以相同的方式使用两者,它们只是性能不同。

IQueryable 仅以有效的方式针对数据库执行。这意味着它创建一个完整的选择查询并仅获取相关记录。

例如,我们想要选取名字以“Nimal”开头的前 10 位客户。在这种情况下,选择查询将生成为 select top 10 * from Customer where name like 'Nimal%'

但如果我们使用 IEnumerable,查询将类似于 select * from Customer where name like 'Nimal%' 并且前十名将在 C# 编码级别进行过滤(它从数据库并将它们传递到 C# 中)。

We can use both for the same way, and they are only different in the performance.

IQueryable only executes against the database in an efficient way. It means that it creates an entire select query and only gets the related records.

For example, we want to take the top 10 customers whose name start with ‘Nimal’. In this case the select query will be generated as select top 10 * from Customer where name like ‘Nimal%’.

But if we used IEnumerable, the query would be like select * from Customer where name like ‘Nimal%’ and the top ten will be filtered at the C# coding level (it gets all the customer records from the database and passes them into C#).

淡忘如思 2024-09-09 16:38:15

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

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

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

I recently ran into an issue with IEnumerable v. IQueryable. The algorithm being used first performed an IQueryable query to obtain a set of results. These were then passed to a foreach loop, with the items instantiated as an Entity Framework (EF) class. This EF class was then used in the from clause of a Linq to Entity query, causing the result to be IEnumerable.

I'm fairly new to EF and Linq for Entities, so it took a while to figure out what the bottleneck was. Using MiniProfiling, I found the query and then converted all of the individual operations to a single IQueryable Linq for Entities query. The IEnumerable took 15 seconds and the IQueryable took 0.5 seconds to execute. There were three tables involved and, after reading this, I believe that the IEnumerable query was actually forming a three table cross-product and filtering the results.

Try to use IQueryables as a rule-of-thumb and profile your work to make your changes measurable.

剩余の解释 2024-09-09 16:38:12

由于看似相互矛盾的反应(主要围绕 IEnumerable),我想澄清一些事情。

(1) IQueryable 扩展了 IEnumerable 接口。 (您可以将 IQueryable 发送到需要 IEnumerable 的对象,而不会出现错误。)

(2) IQueryableIEnumerable LINQ 在迭代结果集时尝试延迟加载。 (请注意,可以在每种类型的接口扩展方法中看到实现。)

换句话说,IEnumerables 并不完全是“内存中”的。 IQueryables 并不总是在数据库上执行。 IEnumerable 必须将内容加载到内存中(一旦检索,可能是延迟加载),因为它没有抽象数据提供程序。 IQueryables 依赖于抽象提供程序(如 LINQ-to-SQL),尽管这也可能是 .NET 内存中提供程序。

示例用例

(a) 从 EF 上下文中以 IQueryable 形式检索记录列表。 (内存中没有记录。)

(b) 将 IQueryable 传递给模型为 IEnumerable 的视图。 (有效。IQueryable 扩展了IEnumerable。)

(c) 从视图迭代并访问数据集的记录、子实体和属性。 (可能会导致异常!)

可能的问题

(1) IEnumerable 尝试延迟加载,并且您的数据上下文已过期。由于提供者不再可用而引发异常。

(2) 实体框架实体代理已启用(默认),并且您尝试访问具有过期数据上下文的相关(虚拟)对象。与(1)相同。

(3) 多个活动结果集 (MARS)。如果您在 foreach( var record in resultSet ) 块中迭代 IEnumerable 并同时尝试访问 record.childEntity.childProperty,那么您由于数据集和关系实体的延迟加载,最终可能会出现 MARS。如果您的连接字符串中未启用它,这将导致异常。

解决方案

  • 我发现在连接字符串中启用 MARS 工作不可靠。我建议你避免使用 MARS,除非它被充分理解并且明确需要。

通过调用 resultList = resultSet.ToList() 执行查询并存储结果这似乎是确保实体位于内存中的最直接方法。

如果您正在访问相关实体,您可能仍然需要数据上下文。或者,您也可以禁用实体代理并从 DbSet 中显式包含相关实体。

I would like to clarify a few things due to seemingly conflicting responses (mostly surrounding IEnumerable).

(1) IQueryable extends the IEnumerable interface. (You can send an IQueryable to something which expects IEnumerable without error.)

(2) Both IQueryable and IEnumerable LINQ attempt lazy loading when iterating over the result set. (Note that implementation can be seen in interface extension methods for each type.)

In other words, IEnumerables are not exclusively "in-memory". IQueryables are not always executed on the database. IEnumerable must load things into memory (once retrieved, possibly lazily) because it has no abstract data provider. IQueryables rely on an abstract provider (like LINQ-to-SQL), although this could also be the .NET in-memory provider.

Sample use case

(a) Retrieve list of records as IQueryable from EF context. (No records are in-memory.)

(b) Pass the IQueryable to a view whose model is IEnumerable. (Valid. IQueryable extends IEnumerable.)

(c) Iterate over and access the data set's records, child entities and properties from the view. (May cause exceptions!)

Possible Issues

(1) The IEnumerable attempts lazy loading and your data context is expired. Exception thrown because provider is no longer available.

(2) Entity Framework entity proxies are enabled (the default), and you attempt to access a related (virtual) object with an expired data context. Same as (1).

(3) Multiple Active Result Sets (MARS). If you are iterating over the IEnumerable in a foreach( var record in resultSet ) block and simultaneously attempt to access record.childEntity.childProperty, you may end up with MARS due to lazy loading of both the data set and the relational entity. This will cause an exception if it is not enabled in your connection string.

Solution

  • I have found that enabling MARS in the connection string works unreliably. I suggest you avoid MARS unless it is well-understood and explicitly desired.

Execute the query and store results by invoking resultList = resultSet.ToList() This seems to be the most straightforward way of ensuring your entities are in-memory.

In cases where the you are accessing related entities, you may still require a data context. Either that, or you can disable entity proxies and explicitly Include related entities from your DbSet.

蓝梦月影 2024-09-09 16:38:08

“IEnumerable”和“IQueryable”之间的主要区别在于过滤器逻辑的执行位置。一个在客户端(在内存中)执行,另一个在数据库上执行。

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

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

The main difference between “IEnumerable” and “IQueryable” is about where the filter logic is executed. One executes on the client side (in memory) and the other executes on the database.

For example, we can consider an example where we have 10,000 records for a user in our database and let's say only 900 out which are active users, so in this case if we use “IEnumerable” then first it loads all 10,000 records in memory and then applies the IsActive filter on it which eventually returns the 900 active users.

While on the other hand on the same case if we use “IQueryable” it will directly apply the IsActive filter on the database which directly from there will return the 900 active users.

城歌 2024-09-09 16:38:05

有一篇博客文章提供了简短的源代码示例,介绍了 IEnumerable 的滥用如何极大地影响 LINQ 查询性能:实体框架:IQueryable 与 IEnumerable

如果我们深入挖掘并查看源代码,我们可以看到 IEnumerable:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

IQueryable

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

执行 了明显不同的扩展方法:第一个返回可枚举迭代器,第二个通过 IQueryable 源中指定的查询提供程序创建查询。

There is a blog post with brief source code sample about how misuse of IEnumerable<T> can dramatically impact LINQ query performance: Entity Framework: IQueryable vs. IEnumerable.

If we dig deeper and look into the sources, we can see that there are obviously different extension methods are perfomed for IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

and IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

The first one returns enumerable iterator, and the second one creates query through the query provider, specified in IQueryable source.

卸妝后依然美 2024-09-09 16:38:02

一般来说,您希望保留查询的原始静态类型,直到它变得重要为止。

因此,您可以将变量定义为“var”,而不是 IQueryable<>IEnumerable<>,并且您将知道您没有更改类型。

如果您一开始使用的是 IQueryable<>,您通常希望将其保留为 IQueryable<>,直到有令人信服的理由对其进行更改。这样做的原因是您希望为查询处理器提供尽可能多的信息。例如,如果您只打算使用 10 个结果(您已调用 Take(10)),那么您希望 SQL Server 了解这一点,以便它可以优化其查询计划并向您发送仅您将使用的数据。

将类型从 IQueryable<> 更改为 IEnumerable<> 的一个令人信服的原因可能是您正在调用一些扩展函数来实现 IQueryable<> ; 在您的特定对象中要么无法处理,要么处理效率低下。在这种情况下,您可能希望将类型转换为 IEnumerable<>(通过分配给 IEnumerable<> 类型的变量或使用 AsEnumerable 扩展方法(例如),以便您调用的扩展函数最终成为 Enumerable 类中的函数,而不是 Queryable 类中的函数。

In general you want to preserve the original static type of the query until it matters.

For this reason, you can define your variable as 'var' instead of either IQueryable<> or IEnumerable<> and you will know that you are not changing the type.

If you start out with an IQueryable<>, you typically want to keep it as an IQueryable<> until there is some compelling reason to change it. The reason for this is that you want to give the query processor as much information as possible. For example, if you're only going to use 10 results (you've called Take(10)) then you want SQL Server to know about that so that it can optimize its query plans and send you only the data you'll use.

A compelling reason to change the type from IQueryable<> to IEnumerable<> might be that you are calling some extension function that the implementation of IQueryable<> in your particular object either cannot handle or handles inefficiently. In that case, you might wish to convert the type to IEnumerable<> (by assigning to a variable of type IEnumerable<> or by using the AsEnumerable extension method for example) so that the extension functions you call end up being the ones in the Enumerable class instead of the Queryable class.

满栀 2024-09-09 16:38:00

前面已经说了很多,但回到根源,以更技术的方式:

  1. IEnumerable 是内存中可以枚举的对象集合 - 内存中的对象使迭代成为可能的序列(使得在 foreach 循环中变得容易,尽管您只能使用 IEnumerator)。它们原样驻留在内存中。
  2. IQueryable 是一个表达式树,它将在某个时刻被转换为其他东西能够枚举最终结果。我想这是大多数人的困惑。

它们显然具有不同的内涵。

IQueryable 表示一个表达式树(简单来说是一个查询),一旦调用发布 API,底层查询提供程序就会将其转换为其他内容,例如 LINQ 聚合函数(Sum、Count 等)或 ToList[数组,字典,...]。并且 IQueryable 对象还实现 IEnumerableIEnumerable,因此如果它们表示查询< /strong> 该查询的结果可以迭代。这意味着 IQueryable 不必只是查询。正确的术语是它们是表达式树

现在这些表达式如何执行以及它们转向什么都取决于所谓的查询提供者(我们可以认为它们是表达式执行器)。

Entity Framework 世界中(即神秘的底层数据源提供程序,或查询提供程序) ) IQueryable 表达式转换为本机 T-SQL 查询。 Nhibernate 对它们做了类似的事情。您可以按照 LINQ:构建 IQueryable 提供程序 链接,您可能希望为您的产品商店提供程序服务提供自定义查询 API。

所以基本上,IQueryable 对象一直在构建,直到我们显式释放它们并告诉系统将它们重写为 SQL 或其他内容,然后发送到执行链以进行后续处理。

就像延迟执行一样,它是一个 LINQ 功能,可以将表达式树方案保存在内存中,并仅根据需要将其发送到执行中,每当针对序列调用某些 API(相同的 Count、ToList 等)时。

两者的正确使用在很大程度上取决于您在特定情况下面临的任务。对于众所周知的存储库模式,我个人选择返回 IList,即 IEnumerable 而不是列表(索引器等)。因此,我的建议是仅在存储库中使用 IQueryable,而在代码的其他任何地方使用 IEnumerable。没有说 IQueryable 崩溃并破坏关注点分离IQueryable的可测试性问题a> 原则。如果您从存储库中返回一个表达式,消费者可以按照自己的意愿使用持久层。

混乱中又添了一点:)(来自评论中的讨论))
它们都不是内存中的对象,因为它们本身不是真正的类型,它们是类型的标记 - 如果您想深入了解的话。但这是有道理的(这就是为什么 MSDN 这么说)将 IEnumerables 视为-内存集合,而 IQueryables 作为表达式树。要点是 IQueryable 接口继承了 IEnumerable 接口,因此如果它表示一个查询,则可以枚举该查询的结果。枚举导致执行与 IQueryable 对象关联的表达式树。
因此,事实上,如果没有内存中的对象,您就无法真正调用任何 IEnumerable 成员。如果你这样做的话它就会进入那里,无论如何,如果它不是空的。 IQueryables 只是查询,而不是数据。

A lot has been said previously, but back to the roots, in a more technical way:

  1. IEnumerable is a collection of objects in memory that you can enumerate - an in-memory sequence that makes it possible to iterate through (makes it way easy for within foreach loop, though you can go with IEnumerator only). They reside in the memory as is.
  2. IQueryable is an expression tree that will get translated into something else at some point with ability to enumerate over the final outcome. I guess this is what confuses most people.

They obviously have different connotations.

IQueryable represents an expression tree (a query, simply) that will be translated to something else by the underlying query provider as soon as release APIs are called, like LINQ aggregate functions (Sum, Count, etc.) or ToList[Array, Dictionary,...]. And IQueryable objects also implement IEnumerable, IEnumerable<T> so that if they represent a query the result of that query could be iterated. It means IQueryable don't have to be queries only. The right term is they are expression trees.

Now how those expressions are executed and what they turn to is all up to so called query providers (expression executors we can think them of).

In the Entity Framework world (which is that mystical underlying data source provider, or the query provider) IQueryable expressions are translated into native T-SQL queries. Nhibernate does similar things with them. You can write your own one following the concepts pretty well described in LINQ: Building an IQueryable Provider link, for example, and you might want to have a custom querying API for your product store provider service.

So basically, IQueryable objects are getting constructed all the way long until we explicitly release them and tell the system to rewrite them into SQL or whatever and send down the execution chain for onward processing.

As if to deferred execution it's a LINQ feature to hold up the expression tree scheme in the memory and send it into the execution only on demand, whenever certain APIs are called against the sequence (the same Count, ToList, etc.).

The proper usage of both heavily depends on the tasks you're facing for the specific case. For the well-known repository pattern I personally opt for returning IList, that is IEnumerable over Lists (indexers and the like). So it is my advice to use IQueryable only within repositories and IEnumerable anywhere else in the code. Not saying about the testability concerns that IQueryable breaks down and ruins the separation of concerns principle. If you return an expression from within repositories consumers may play with the persistence layer as they would wish.

A little addition to the mess :) (from a discussion in the comments))
None of them are objects in memory since they're not real types per se, they're markers of a type - if you want to go that deep. But it makes sense (and that's why even MSDN put it this way) to think of IEnumerables as in-memory collections whereas IQueryables as expression trees. The point is that the IQueryable interface inherits the IEnumerable interface so that if it represents a query, the results of that query can be enumerated. Enumeration causes the expression tree associated with an IQueryable object to be executed.
So, in fact, you can't really call any IEnumerable member without having the object in the memory. It will get in there if you do, anyways, if it's not empty. IQueryables are just queries, not the data.

困倦 2024-09-09 16:37:58

一般来说,我建议如下:

  • 如果您希望开发人员能够使用您的方法在执行之前优化您返回的查询,则返回 IQueryable

  • 如果您想传输一组要枚举的对象,请返回IEnumerable

想象一下 IQueryable 它是什么 - 数据的“查询”(如果您愿意,您可以对其进行细化)。 IEnumerable 是一组可以枚举的对象(已接收或已创建)。

In general terms I would recommend the following:

  • Return IQueryable<T> if you want to enable the developer using your method to refine the query you return before executing.

  • Return IEnumerable if you want to transport a set of Objects to enumerate over.

Imagine an IQueryable as that what it is - a "query" for data (which you can refine if you want to). An IEnumerable is a set of objects (which has already been received or was created) over which you can enumerate.

活泼老夫 2024-09-09 16:37:55

两者都会给你延迟执行,是的。

至于哪个优先于另一个,这取决于您的基础数据源是什么。

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

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

Both will give you deferred execution, yes.

As for which is preferred over the other, it depends on what your underlying datasource is.

Returning an IEnumerable will automatically force the runtime to use LINQ to Objects to query your collection.

Returning an IQueryable (which implements IEnumerable, by the way) provides the extra functionality to translate your query into something that might perform better on the underlying source (LINQ to SQL, LINQ to XML, etc.).

假面具 2024-09-09 16:37:53

是的,两者都使用延迟执行。让我们使用 SQL Server 探查器来说明差异......

当我们运行以下代码时:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

在 SQL Server 探查器中,我们发现一个命令等于:

"SELECT * FROM [dbo].[WebLog]"

针对具有 100 万个 WebLog 表运行该代码块大约需要 90 秒记录。

因此,所有表记录都作为对象加载到内存中,然后对于每个 .Where() ,它将成为内存中针对这些对象的另一个过滤器。

当我们在上面的示例(第二行)中使用 IQueryable 而不是 IEnumerable 时:

在 SQL Server Profiler 中,我们发现一个命令等于:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

运行此块大约需要四秒使用 IQueryable 的代码。

IQueryable 有一个名为 Expression 的属性,它存储一个树表达式,当我们在示例中使用 result 时开始创建该表达式(称为延迟执行),最后表达式将转换为 SQL 查询以在数据库引擎上运行。

Yes, both use deferred execution. Let's illustrate the difference using the SQL Server profiler....

When we run the following code:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

In SQL Server profiler we find a command equal to:

"SELECT * FROM [dbo].[WebLog]"

It approximately takes 90 seconds to run that block of code against a WebLog table which has 1 million records.

So, all table records are loaded into memory as objects, and then with each .Where() it will be another filter in memory against these objects.

When we use IQueryable instead of IEnumerable in the above example (second line):

In SQL Server profiler we find a command equal to:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

It approximately takes four seconds to run this block of code using IQueryable.

IQueryable has a property called Expression which stores a tree expression which starts being created when we used the result in our example (which is called deferred execution), and at the end this expression will be converted to an SQL query to run on the database engine.

满天都是小星星 2024-09-09 16:37:51

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

  • IEnumerable 版本签名为: Where(Func predicate)

  • IQueryable 版本签名为:Where(Expression> predicate)

您可能一直在使用以下两者那些没有意识到的,因为两者都使用相同的语法调用:

例如 Where(x => x.City == "") 适用于 IEnumerableIQueryable

  • IEnumerable 集合上使用 Where() 时,编译器会将编译后的函数传递给 Where()

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

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

哦,这听起来像是免费的性能提升,在这种情况下我应该在各处使用 AsQueryable() 吗?
不,IQueryable 仅当底层数据提供程序可以使用它执行某些操作时才有用。将常规 List 之类的内容转换为 IQueryable 不会给您带来任何好处。

The top answer is good but it doesn't mention expression trees which explain "how" the two interfaces differ. Basically, there are two identical sets of LINQ extensions. Where(), Sum(), Count(), FirstOrDefault(), etc all have two versions: one that accepts functions and one that accepts expressions.

  • The IEnumerable version signature is: Where(Func<Customer, bool> predicate)

  • The IQueryable version signature is: Where(Expression<Func<Customer, bool>> predicate)

You've probably been using both of those without realizing it because both are called using identical syntax:

e.g. Where(x => x.City == "<City>") works on both IEnumerable and IQueryable

  • When using Where() on an IEnumerable collection, the compiler passes a compiled function to Where()

  • When using Where() on an IQueryable collection, the compiler passes an expression tree to Where(). An expression tree is like the reflection system but for code. The compiler converts your code into a data structure that describes what your code does in a format that's easily digestible.

Why bother with this expression tree thing? I just want Where() to filter my data.
The main reason is that both the EF and Linq2SQL ORMs can convert expression trees directly into SQL where your code will execute much faster.

Oh, that sounds like a free performance boost, should I use AsQueryable() all over the place in that case?
No, IQueryable is only useful if the underlying data provider can do something with it. Converting something like a regular List to IQueryable will not give you any benefit.

や莫失莫忘 2024-09-09 16:37:49

是的,两者都会为您提供延迟执行

区别在于 IQueryable是允许 LINQ-to-SQL(实际上是 LINQ.-to-anything)工作的接口。因此,如果您进一步优化 IQueryable,如果可能的话,该查询将在数据库中执行。

对于 IEnumerable 情况,它将是 LINQ 到对象,这意味着与原始查询匹配的所有对象都必须从数据库加载到内存中。

在代码中:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

该代码将仅对选定的黄金客户执行 SQL。另一方面,以下代码将执行数据库中的原始查询,然后过滤掉内存中的非黄金客户:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

这是一个非常重要的区别,并且正在处理 IQueryable 在许多情况下可以避免从数据库返回太多行。另一个主要例子是进行分页:如果您使用 Take< /a> 和 跳过 IQueryable,您只会得到请求的行数;在 IEnumerable 上执行此操作将导致所有行都加载到内存中。

Yes, both will give you deferred execution.

The difference is that IQueryable<T> is the interface that allows LINQ-to-SQL (LINQ.-to-anything really) to work. So if you further refine your query on an IQueryable<T>, that query will be executed in the database, if possible.

For the IEnumerable<T> case, it will be LINQ-to-object, meaning that all objects matching the original query will have to be loaded into memory from the database.

In code:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

That code will execute SQL to only select gold customers. The following code, on the other hand, will execute the original query in the database, then filtering out the non-gold customers in the memory:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

This is quite an important difference, and working on IQueryable<T> can in many cases save you from returning too many rows from the database. Another prime example is doing paging: If you use Take and Skip on IQueryable, you will only get the number of rows requested; doing that on an IEnumerable<T> will cause all of your rows to be loaded in memory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文