我在理解 IQueryable<T> 时遇到问题
Posted
技术标签:
【中文标题】我在理解 IQueryable<T> 时遇到问题【英文标题】:I'm having problems understanding IQueryable<T> 【发布时间】:2011-03-17 13:27:18 【问题描述】:所以我想了解IQueryable<T>
。我正在阅读的教程建议使用它,但不确定为什么。该代码仅使用 LINQ to SQL 返回一些值。我过去做过很多次,但没有使用IQueryable<T>
为什么将它与返回超过 1 个值的函数一起使用?
这是我的代码:
public IQueryable<Items> GetItems()
return from item in db.Items
where item.IsActive == true
orderby item.ItemNumber
select item;
【问题讨论】:
好问题。你为我节省了数小时的研究时间。 【参考方案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<T>
是一个IEnumerable<T>
,当您尝试对其调用GetEnumerator()
方法时,它的提供程序会计算最终的 SQL。您可以通过调用它来显式执行此操作,也可以通过在 foreach
语句中使用它来隐式执行此操作。此外,ToArray()
之类的扩展方法将在内部执行其中之一,从而产生相同的效果。
【讨论】:
IEnumerable 也是如此 - 它只会被评估foreach
或 .ToArray()
被调用。那么为什么 IQueryable 与 IEnumerable 不同呢?
一个 IQueryable is 一个 IEnumerable,它们都可以提供一个后期绑定的集合。但是,L2S 中 IQuerable 的意义在于延迟 SQL 执行。使用 IQueryable,您可以继续添加查询元素,例如 .Where() 条件,直到您最终开始枚举为止。此时,IQueryable 最终执行 SQL 查询并填充一个集合供您枚举。如果您确定您已完成查询并且调用您的方法不需要添加更多条件,那么您可能决定只调用 .ToList() 并在不返回 IQuerable 的情况下完成。
IQueryable<T>
实现了IEnumerable<T>
接口,因此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,但您可以添加增长表达式树的子句。以上是关于我在理解 IQueryable<T> 时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章
如何检查 IQueryable<T>.Element 类型是不是为接口
Enumerable.Empty<T>() 等效于 IQueryable
IQueryable 和 IQueryable<T> 是异步的吗?
NSubstitute DbSet / IQueryable<T>