编写第一个 LINQ 查询 (Visual Basic)
查询是一种从数据源检索数据的表达式。查询用专用查询语言表示。随着时间的推移,人们已经为不同类型的数据源开发了不同的语言,例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。这使应用程序开发人员必须针对所支持的每种数据源或数据格式而学习新的查询语言。
语言集成查询 (LINQ) 通过提供一种跨各种数据源和数据格式使用数据的一致模型,简化了这一情况。在 LINQ 查询中,始终会用到对象。在查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集和实体、.NET Framework 集合中的数据以及具有相应的 LINQ 提供程序的任何其他源或格式的数据时,都会使用相同的基本编码模式。本文档描述创建和使用基本 LINQ 查询的三个阶段。
若要观看相关的视频演示,请参见 How Do I: Get Started with 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>,包括泛型 List<T>,Dictionary<TKey, TValue>的任何集合类型,因此,在 .NET framework 的其他选件类库。
如果源数据未实现 IEnumerable<T>,LINQ 提供程序需要的实现 标准查询运算符的 功能该数据源的。例如,LINQ to XML 处理将 XML 文档加载到可查询的 XElement 类型中的工作,如下面的示例所示。有关标准查询运算符的更多信息,请参见标准查询运算符概述。
' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")
在 LINQ to SQL 中,首先手动或使用 Object Relational Designer (O/R Designer) 在设计时创建对象关系映射。针对这些对象编写查询,然后由 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(语言集成查询)。)基本规则很简单:LINQ 数据源是支持泛型 IEnumerable<T> 接口或从该接口继承的接口的任意对象。
说明 |
---|
支持非泛型 IEnumerable 接口的类型(如 ArrayList)也可用作 LINQ 数据源。有关使用 ArrayList 的示例,请参见如何:使用 LINQ 查询 ArrayList。 |
查询
在查询中,指定要从数据源中检索的信息。还可以指定在返回这些信息之前如何对其进行排序、分组或结构化。为了便于创建查询,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) 中进行了讨论。有关更多信息,请参见查询 (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 方法)执行查询。
相关视频演示
How Do I: Get Started with LINQ?(如何实现:LINQ 入门)
Video How to: Writing Queries in Visual Basic(视频帮助主题:在 Visual Basic 中编写查询)