查询是从数据源检索数据的表达式。 查询以专用查询语言表示。 随着时间的推移,为不同类型的数据源开发了不同的语言,例如关系数据库的 SQL 和用于 XML 的 XQuery。 这使得应用程序开发人员有必要为支持的每种数据源或数据格式学习新的查询语言。
Language-Integrated 查询(LINQ)提供了一致的模型,用于处理各种数据源和格式的数据,从而简化了情况。 在 LINQ 查询中,始终使用对象。 使用相同的基本编码模式查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集和实体、.NET Framework 集合以及 LINQ 提供程序可用的任何其他源或格式中的数据。 本文档介绍创建和使用基本 LINQ 查询的三个阶段。
查询操作的三个阶段
LINQ 查询操作由三个步骤组成:
获取数据源。
创建查询。
执行查询。
在 LINQ 中,查询的执行不同于查询的创建。 仅通过创建查询无法检索任何数据。 本主题稍后将更详细地讨论这一点。
以下示例演示查询作的三个部分。 该示例使用整数数组作为方便的数据源进行演示。 但是,相同的概念也适用于其他数据源。
注释
在“编译”页的“项目设计器 (Visual Basic)”中,确保“Option infer”设置为“打开”。
' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}
' Query creation.
Dim evensQuery = From num In numbers
Where num Mod 2 = 0
Select num
' Query execution.
For Each number In evensQuery
Console.Write(number & " ")
Next
输出:
0 2 4 6
数据源
由于上一示例中的数据源是一个数组,因此它隐式支持泛型 IEnumerable<T> 接口。 通过此事实,可以将数组用作 LINQ 查询的数据源。 支持 IEnumerable<T> 或派生接口的类型(如泛型 IQueryable<T> )称为 可查询类型。
作为隐式可查询类型,数组不需要修改或特殊处理才能用作 LINQ 数据源。 对于支持IEnumerable<T>的任何集合类型,.NET Framework 类库中的泛型List<T>、Dictionary<TKey,TValue>和其他类也是如此。
如果源数据尚未实现 IEnumerable<T>,则需要 LINQ 提供程序来实现该数据源 的标准查询运算符 的功能。 例如,LINQ to XML 处理将 XML 文档加载到可 XElement 查询类型的工作,如以下示例所示。 有关标准查询运算符的详细信息,请参阅标准查询运算符概述(Visual Basic)。
' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")
使用 LINQ to SQL,首先在设计时手动或使用 Visual Studio 中的 LINQ to SQL 工具 创建对象关系映射。 针对对象编写查询,在运行时 LINQ to SQL 处理与数据库的通信。 在下面的示例中, customers
表示数据库中的特定表,并支持 Table<TEntity> 泛型 IQueryable<T>。
' Create a data source from a SQL table.
Dim db As New DataContext("C:\Northwind\Northwnd.mdf")
Dim customers As Table(Of Customer) = db.GetTable(Of Customer)
有关如何创建特定类型的数据源的详细信息,请参阅各种 LINQ 提供程序的文档。 (有关这些提供程序的列表,请参阅 LINQ(Language-Integrated Query)。) 基本规则很简单:LINQ 数据源是支持泛型 IEnumerable<T> 接口的任何对象,或者是从中继承的接口。
注释
支持 ArrayList 非泛型 IEnumerable 接口的类型也可以用作 LINQ 数据源。 有关使用 A ArrayList的示例,请参阅“如何:使用 LINQ 查询 ArrayList”(Visual Basic)。
查询
在查询中,指定要从数据源或源检索哪些信息。 还可以选择指定在返回信息之前应如何对信息进行排序、分组或结构化。 为了启用查询创建,Visual Basic 已将新的查询语法合并到语言中。
执行时,以下示例中的查询返回整数数组 numbers
中的所有偶数。
' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}
' Query creation.
Dim evensQuery = From num In numbers
Where num Mod 2 = 0
Select num
' Query execution.
For Each number In evensQuery
Console.Write(number & " ")
Next
查询表达式包含三个子句: From
、 Where
和 Select
。 在基本查询操作 (Visual Basic)中讨论了每个查询表达式子句的特定功能和用途。 有关详细信息,请参阅 查询。 请注意,在 LINQ 中,查询定义通常存储在变量中,稍后执行。 查询变量(如 evensQuery
上一示例中)必须是可查询类型。
evensQuery
的类型是 IEnumerable(Of Integer)
,由编译器使用本地类型推理分配。
请务必记住,查询变量本身不执行任何作,不返回任何数据。 它仅存储查询定义。 在前面的示例中,执行查询的是 For Each
循环。
查询执行
查询执行独立于查询创建。 查询创建定义查询,但执行是由不同的机制触发的。 只要定义查询(立即执行),就可以执行查询,也可以存储该定义,并且可以稍后执行查询(延迟执行)。
延迟执行
典型的 LINQ 查询类似于上一示例中 evensQuery
定义的查询。 它会创建查询,但不立即执行它。 而是将查询定义存储在查询变量 evensQuery
中。 稍后执行查询,通常是通过使用循环 For Each
(返回值序列)或应用标准查询运算符(例如 Count
或 Max
)。 此过程称为 延迟执行。
' Query execution that results in a sequence of values.
For Each number In evensQuery
Console.Write(number & " ")
Next
' Query execution that results in a single value.
Dim evens = evensQuery.Count()
对于值序列,可以使用循环For Each
中的number
迭代变量(在前面的示例中)访问检索的数据。 由于查询变量 evensQuery
包含查询定义而不是查询结果,因此可以使用查询变量多次执行查询。 例如,应用程序中可能有一个数据库,该数据库由单独的应用程序不断更新。 创建从该数据库检索数据的查询后,可以使用循环 For Each
重复执行查询,每次检索最新数据。
以下示例演示延迟执行的工作原理。
evensQuery2
在使用For Each
循环定义并执行之后,如前面的示例中所示,数据源numbers
中的某些元素发生了变化。 然后,第二个 For Each
循环再次运行 evensQuery2
。 结果第二次不同,因为 For Each
循环使用新 numbers
值再次执行查询。
Dim numberArray() = {0, 1, 2, 3, 4, 5, 6}
Dim evensQuery2 = From num In numberArray
Where num Mod 2 = 0
Select num
Console.WriteLine("Evens in original array:")
For Each number In evensQuery2
Console.Write(" " & number)
Next
Console.WriteLine()
' Change a few array elements.
numberArray(1) = 10
numberArray(4) = 22
numberArray(6) = 8
' Run the same query again.
Console.WriteLine(vbCrLf & "Evens in changed array:")
For Each number In evensQuery2
Console.Write(" " & number)
Next
Console.WriteLine()
输出:
Evens in original array:
0 2 4 6
Evens in changed array:
0 10 2 22 8
立即执行
在延迟执行查询时,查询定义存储在查询变量中供以后执行。 在即时执行中,查询会在定义时立即执行。 应用需要访问查询结果的各个元素的方法时,将触发执行。 直接执行通常通过使用返回单个值的标准查询运算符之一来强制执行。 示例包括 Count
、 Max
、 Average
和 First
。 这些标准查询运算符一旦应用,便立即执行查询,以便计算并返回单个结果。 有关返回单个值的标准查询运算符的详细信息,请参阅 聚合操作、元素操作和 限定符操作。
以下查询返回整数数组中偶数的数量。 查询定义未保存,并且 numEvens
是一个简单的 Integer
。
Dim numEvens = (From num In numbers
Where num Mod 2 = 0
Select num).Count()
可以使用该方法实现相同的结果 Aggregate
。
Dim numEvensAgg = Aggregate num In numbers
Where num Mod 2 = 0
Select num
Into Count()
还可以通过对查询(即时)或查询变量(延迟)调用 ToList
或 ToArray
方法来强制执行查询,如以下代码所示。
' Immediate execution.
Dim evensList = (From num In numbers
Where num Mod 2 = 0
Select num).ToList()
' Deferred execution.
Dim evensQuery3 = From num In numbers
Where num Mod 2 = 0
Select num
' . . .
Dim evensArray = evensQuery3.ToArray()
在前面的示例中, evensQuery3
查询变量是一个列表, evensList
也是 evensArray
一个数组。
在想要立即执行查询并在单个集合对象中缓存结果的情况下,使用 ToList
或 ToArray
强制立即执行尤其有用。 有关这些方法的详细信息,请参阅 “转换数据类型”。
还可以使用IEnumerable
方法(例如IEnumerable.GetEnumerator方法)来执行查询。