【问题标题】:I'm having problems understanding IQueryable<T>我在理解 IQueryable<T> 时遇到问题
【发布时间】:2011-03-17 13:27:18
【问题描述】:

所以我想了解IQueryable&lt;T&gt;。我正在阅读的教程建议使用它,但不确定为什么。该代码仅使用 LINQ to SQL 返回一些值。我过去做过很多次,但没有使用IQueryable&lt;T&gt;

为什么将它与返回超过 1 个值的函数一起使用?

这是我的代码:

public IQueryable<Items> GetItems()
    {
        return from item in db.Items
               where item.IsActive == true
               orderby item.ItemNumber
               select item;
    }

【问题讨论】:

  • 好问题。你为我节省了数小时的研究时间。

标签: c# asp.net collections data-access-layer


【解决方案1】:

IQueryable 将查询表示为表达式树,而不在服务器上对其进行评估。这使您可以在实际生成 SQL 之前指定进一步的处理。

在上述情况下,这意味着您可以对调用 GetItems() 的结果进行处理,并将原始查询和额外内容作为单个查询发送:

var recentItems = from item in GetItems()
                  where item.Timestamp > somedate
                  select item;

foreach (var item in recentItems)
{
    // Do something with an active recent item.
}

在我们尝试使用 foreach 循环中的结果之前,不会向服务器发送任何内容。此时,LINQ-to-SQL 提供程序会评估整个表达式,包括在 GetItems() 内部生成的位和之后指定的位,并发出一条 SQL 语句来选择所有活动的和最近的项目。

为了澄清技术性,IQueryable&lt;T&gt; 是一个IEnumerable&lt;T&gt;,当您尝试对其调用GetEnumerator() 方法时,它的提供程序会计算最终的 SQL。您可以通过调用它来显式执行此操作,也可以通过在 foreach 语句中使用它来隐式执行此操作。此外,ToArray() 之类的扩展方法将在内部执行其中之一,从而产生相同的效果。

【讨论】:

  • IEnumerable 也是如此 - 它只会被评估 foreach.ToArray() 被调用。那么为什么 IQueryable 与 IEnumerable 不同呢?
  • 一个 IQueryable is 一个 IEnumerable,它们都可以提供一个后期绑定的集合。但是,L2S 中 IQuerable 的意义在于延迟 SQL 执行。使用 IQueryable,您可以继续添加查询元素,例如 .Where() 条件,直到您最终开始枚举为止。此时,IQueryable 最终执行 SQL 查询并填充一个集合供您枚举。如果您确定您已完成查询并且调用您的方法不需要添加更多条件,那么您可能决定只调用 .ToList() 并在不返回 IQuerable 的情况下完成。
  • IQueryable&lt;T&gt; 实现了IEnumerable&lt;T&gt; 接口,因此IQueryable 可以用作IEnumerable。最大的区别在于 IQueryable 是操作的定义,而不是操作(编译代码)本身。这允许 LINQ to SQL 提供程序对其进行分析并将其转换为 SQL。这对于普通的编译代码来说(几乎)是不可能的。
【解决方案2】:

IQueryable(在枚举之前)不是它们本身的结果,而是用于返回这些结果的逻辑。

使用 LINQ2SQL 示例...假设您返回一个 IQueryable 客户端。 在幕后,它不会是客户端列表(直到你 ToList() 它),但实际上是一个 SQL 查询,如下所示:

SELECT * FROM [Clients]

现在这很方便,因为我们还没有访问数据库!因此,假设当 IQueriable 回来时,我们希望将其细化为称为“Bob”的客户,我们可以这样做:

var clients = GetClients().Where(c => c.Name == "Bob");

现在 IQueriable 看起来像这样:

SELECT * FROM [Clients] WHERE Name = 'Bob'.

现在,当我执行 clients.ToList() 时,该查询将运行,数据库被命中,并且我有一个名为 bob 的客户端列表,而不必选择所有客户端然后在内存中搜索它们,或者执行 2 个单独的数据库命中。

举个例子,当你的数据上下文超出范围时(例如:在 using 语句中运行你的选择),你会在后面尝试获取子元素。这就是加载选项在 LINQ2SQL 中派上用场的地方。

希望有帮助

【讨论】:

  • IEnumerable 也是如此 - 它只被评估 .ToList() 被调用。那么为什么 IQueryable 与 IEnumerable 不同呢?
  • 在枚举 IQueryable 之前,您的数据仍然是一个表达式树...在 LINQ2SQL 的示例中,您不能将 SELECT * FROM Clients 作为 IEnumerable,因为它是一个表达式并且不包含可枚举的值,直到它自己运行的查询。虽然它仍然是一个未枚举的 IQueryable,但您可以添加增长表达式树的子句。
猜你喜欢
  • 1970-01-01
  • 2011-11-23
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 2021-04-07
  • 1970-01-01
  • 2022-11-25
  • 1970-01-01
相关资源
最近更新 更多