Visual Basic 中的 LINQ 简介

语言集成查询 (LINQ) 向 Visual Basic 添加了查询功能,能够为处理各种数据提供简单而强大的功能。 LINQ 不将查询发送到数据库来处理,也不对搜索的每种类型的数据使用不同的查询语法,而是将查询作为 Visual Basic 语言的一部分引入。 它使用统一语法,而不考虑数据的类型。

通过 LINQ,可以查询来自 SQL Server 数据库、XML、内存中数组和集合、ADO.NET 数据集或支持 LINQ 的任何其他远程或本地数据源的数据。 可以使用通用 Visual Basic 语言元素执行此操作。 因为查询是用 Visual Basic 语言编写的,所以查询结果返回为强类型化对象。 这些对象支持 IntelliSense,这使你能够更快地编写代码,并在编译时(而不是在运行时)捕获查询中的错误。 可将 LINQ 查询用作其他查询的源,以便缩小结果的范围。 还可将它们绑定到控件,使用户能够轻松地查看和修改查询结果。

例如,下面的代码示例显示一个 LINQ 查询,它从集合返回一个客户列表并根据位置对其进行分组。

' Obtain a list of customers.
Dim customers As List(Of Customer) = GetCustomers()

' Return customers that are grouped based on country.
Dim countries = From cust In customers
                Order By cust.Country, cust.City
                Group By CountryName = cust.Country
                Into CustomersInCountry = Group, Count()
                Order By CountryName

' Output the results.
For Each country In countries
    Debug.WriteLine(country.CountryName & " count=" & country.Count)

    For Each customer In country.CustomersInCountry
        Debug.WriteLine("   " & customer.CompanyName & "  " & customer.City)
    Next
Next

' Output:
'   Canada count=2
'      Contoso, Ltd  Halifax
'      Fabrikam, Inc.  Vancouver
'   United States count=1
'      Margie's Travel  Redmond

运行示例

若要运行简介中和 LINQ 查询的结构部分中的示例,请包含以下代码,它将返回客户和订单的列表。

' Return a list of customers.
Private Function GetCustomers() As List(Of Customer)
    Return New List(Of Customer) From
        {
            New Customer With {.CustomerID = 1, .CompanyName = "Contoso, Ltd", .City = "Halifax", .Country = "Canada"},
            New Customer With {.CustomerID = 2, .CompanyName = "Margie's Travel", .City = "Redmond", .Country = "United States"},
            New Customer With {.CustomerID = 3, .CompanyName = "Fabrikam, Inc.", .City = "Vancouver", .Country = "Canada"}
        }
End Function

' Return a list of orders.
Private Function GetOrders() As List(Of Order)
    Return New List(Of Order) From
        {
            New Order With {.CustomerID = 1, .Amount = "200.00"},
            New Order With {.CustomerID = 3, .Amount = "600.00"},
            New Order With {.CustomerID = 1, .Amount = "300.00"},
            New Order With {.CustomerID = 2, .Amount = "100.00"},
            New Order With {.CustomerID = 3, .Amount = "800.00"}
        }
End Function

' Customer Class.
Private Class Customer
    Public Property CustomerID As Integer
    Public Property CompanyName As String
    Public Property City As String
    Public Property Country As String
End Class

' Order Class.
Private Class Order
    Public Property CustomerID As Integer
    Public Property Amount As Decimal
End Class

LINQ 提供程序

LINQ 提供程序将 Visual Basic LINQ 查询映射到被查询的数据源。 编写 LINQ 查询时,该提供程序将接受该查询,并将其转换为数据源能够执行的命令。 该提供程序还将来自源的数据转换为构成查询结果的对象。 最后,当你将更新发送至数据源时,它又将这些对象转换为数据。

Visual Basic 包括以下 LINQ 提供程序。

提供程序 描述
LINQ to Objects 通过 LINQ to Objects 提供程序,可以查询内存中集合和数组。 如果对象支持 IEnumerableIEnumerable<T> 接口,则可以通过 LINQ to Objects 提供程序对其进行查询。

可以通过导入 System.Linq 命名空间来启用 LINQ to Objects 提供程序,默认情况下,将为所有 Visual Basic 项目导入此命名空间。

有关 LINQ to Objects 提供程序的详细信息,请参阅 LINQ to Objects
LINQ to SQL 通过 LINQ to SQL 提供程序,可以查询和修改 SQL Server 数据库中的数据。 这样就可以轻松将应用程序的对象模型映射到数据库中的表和对象。

Visual Basic 通过包括对象关系设计器(O/R 设计器)使其能更轻松地使用 LINQ to SQL。 此设计器用于在应用程序中创建映射到数据库中的对象的对象模型。 O/R 设计器还提供一项功能,用于将存储过程和函数映射到 DataContext 对象,该对象可管理与数据库的通信和存储乐观并发检查的状态。

有关 LINQ to SQL 提供程序的详细信息,请参阅 LINQ to SQL。 有关对象关系设计器的详细信息,请参阅 Visual Studio 中的 LINQ to SQL 工具
LINQ to XML 通过 LINQ to XML 提供程序,可以查询和修改 XML。 可以修改内存中 XML,也可以从文件加载 XML 以及将 XML 保存到文件。

此外,LINQ to XML 提供程序还包括 XML 文本和 XML 轴属性,通过它们,你可以在 Visual Basic 代码中直接编写 XML。 有关更多信息,请参见 XML
LINQ to DataSet 通过 LINQ to DataSet 提供程序,可以查询和更新 ADO.NET 数据集中的数据。 可以将 LINQ 的强大功能添加到使用数据集的应用程序,以便简化和扩展查询、聚合和更新数据集中数据的功能。

有关详细信息,请参阅 LINQ to DataSet

LINQ 查询的结构

LINQ 查询(通常称为“查询表达式”)由标识数据源和查询的迭代变量的查询子句组合而成。 查询表达式还可以包含排序、筛选、分组和联接的说明或要对源数据应用的计算。 查询表达式语法类似于 SQL的语法;因此,你可能发现该语法大都非常熟悉。

查询表达式以 From 子句开头。 此子句标识查询的源数据和用于分别表示源数据中每个元素的变量。 这些变量叫做“范围变量”或“迭代变量”。 From 子句是查询所必需的,但 Aggregate 查询除外,在该查询中 From 子句是可选的。 在 FromAggregate 子句中标识查询的范围和源后,即可包括查询子句的任意组合来优化查询。 有关查询子句的详细信息,请参阅本主题中稍后介绍的 Visual Basic LINQ 查询运算符。 例如,下面的查询将客户数据源集合标识为 customers 变量和一个名为 cust 的迭代变量。

Dim customers = GetCustomers()

Dim queryResults = From cust In customers

For Each result In queryResults
    Debug.WriteLine(result.CompanyName & "  " & result.Country)
Next

' Output:
'   Contoso, Ltd  Canada
'   Margie's Travel  United States
'   Fabrikam, Inc.  Canada

此示例本身是一个有效查询;但是,如果添加更多查询子句以优化结果,查询将变得更加强大。 例如,可以添加 Where 子句,以便按一个或多个值筛选结果。 查询表达式都是单行代码;只能将其他查询子句附加到查询的末尾。 可以使用下划线 (_) 续行符将查询拆散为多行文本,以提高可读性。 下面的代码示例演示了包含 Where 子句的查询示例。

Dim queryResults = From cust In customers
                   Where cust.Country = "Canada"

另一个功能强大的查询子句是 Select 子句,它能够从数据源仅返回所选字段。 LINQ 查询返回强类型化对象的可枚举集合。 查询可以返回匿名类型或具名类型的集合。 可以使用 Select 子句从数据源仅返回单个字段。 执行此操作时,返回的集合类型为该单个字段的类型。 也可以使用 Select 子句从数据源返回多个字段。 执行此操作时,返回的集合类型为新的匿名类型。 还可以将由查询返回的字段和指定的具名类型字段进行匹配。 下面的代码示例演示了一个查询表达式,它返回一个匿名类型的集合,该集合具有使用来自数据源中所选字段的数据填充的成员。

Dim queryResults = From cust In customers
               Where cust.Country = "Canada"
               Select cust.CompanyName, cust.Country

还可使用 LINQ 查询合并多个数据源组并返回单个结果。 可以使用一个或多个 From 子句,或者使用 JoinGroup Join 查询子句来完成此操作。 下面的代码示例演示了一个查询表达式,它合并客户和订单数据,并返回包含客户和订单数据的匿名类型的集合。

Dim customers = GetCustomers()
Dim orders = GetOrders()

Dim queryResults = From cust In customers, ord In orders
           Where cust.CustomerID = ord.CustomerID
           Select cust, ord

For Each result In queryResults
    Debug.WriteLine(result.ord.Amount & "  " & result.ord.CustomerID & "  " & result.cust.CompanyName)
Next

' Output:
'   200.00  1  Contoso, Ltd
'   300.00  1  Contoso, Ltd
'   100.00  2  Margie's Travel
'   600.00  3  Fabrikam, Inc.
'   800.00  3  Fabrikam, Inc.

可以使用 Group Join 子句创建包含客户对象集合的分层查询结果。 每个客户对象都具有一个包含该客户所有订单的集合的属性。 下面的代码示例演示了一个查询表达式,它将客户和订单数据合并为分层结果,并返回一个匿名类型的集合。 查询返回一个包括 CustomerOrders 属性的类型,该属性包含客户订单数据的集合。 它还包括 OrderTotal 属性,该属性包含客户所有订单的总计值之和。 (此查询等效于 LEFT OUTER JOIN。)

Dim customers = GetCustomers()
Dim orders = GetOrders()

Dim queryResults = From cust In customers
                   Group Join ord In orders On
                     cust.CustomerID Equals ord.CustomerID
                     Into CustomerOrders = Group,
                          OrderTotal = Sum(ord.Amount)
                   Select cust.CompanyName, cust.CustomerID,
                          CustomerOrders, OrderTotal

For Each result In queryResults
    Debug.WriteLine(result.OrderTotal & "  " & result.CustomerID & "  " & result.CompanyName)
    For Each ordResult In result.CustomerOrders
        Debug.WriteLine("   " & ordResult.Amount)
    Next
Next

' Output:
'   500.00  1  Contoso, Ltd
'      200.00
'      300.00
'   100.00  2  Margie's Travel
'      100.00
'   1400.00  3  Fabrikam, Inc.
'      600.00
'      800.00

还有几个其他 LINQ 查询运算符,可用它们创建功能强大的查询表达式。 本主题的下一部分将讨论查询表达式中可以包括的各种查询子句。 有关 Visual Basic 查询子句的详细信息,请参阅查询

Visual Basic LINQ 查询运算符

你可以调用 System.Linq 命名空间和支持 LINQ 查询的其他命名空间中的类包括的方法,以便根据应用程序的需要创建和完善查询。 Visual Basic 包含以下常见查询子句的关键字。 有关 Visual Basic 查询子句的详细信息,请参阅查询

From 子句

需要 From 子句Aggregate 子句才能开始查询。 From 子句可指定查询的源集合和迭代变量。 例如:

' Returns the company name for all customers for which
' the Country is equal to "Canada".
Dim names = From cust In customers
            Where cust.Country = "Canada"
            Select cust.CompanyName

Select 子句

可选。 Select 子句 声明查询的一组迭代变量。 例如:

' Returns the company name and ID value for each
' customer as a collection of a new anonymous type.
Dim customerList = From cust In customers
                   Select cust.CompanyName, cust.CustomerID

如果未指定 Select 子句,则该查询的迭代变量将由 FromAggregate 子句指定的迭代变量组成。

Where 子句

可选。 Where 子句指定查询的筛选条件。 例如:

' Returns all product names for which the Category of
' the product is "Beverages".
Dim names = From product In products
            Where product.Category = "Beverages"
            Select product.Name

Order By 子句

可选。 Order By 子句指定查询中列的排列顺序。 例如:

' Returns a list of books sorted by price in 
' ascending order.
Dim titlesAscendingPrice = From b In books
                           Order By b.price

Join 子句

可选。 Join 子句将两个集合合并为单个集合。 例如:

' Returns a combined collection of all of the 
' processes currently running and a descriptive
' name for the process taken from a list of 
' descriptive names.
Dim processes = From proc In Process.GetProcesses
                Join desc In processDescriptions
                  On proc.ProcessName Equals desc.ProcessName
                Select proc.ProcessName, proc.Id, desc.Description

Group By 子句

可选。 Group By 子句对查询结果的元素进行分组。 它可用于将聚合函数应用到每个组。 例如:

' Returns a list of orders grouped by the order date
' and sorted in ascending order by the order date.
Dim orderList = From order In orders
                Order By order.OrderDate
                Group By OrderDate = order.OrderDate
                Into OrdersByDate = Group

Group Join 子句

可选。 Group Join 子句将两个集合合并为单个分层集合。 例如:

' Returns a combined collection of customers and
' customer orders.
Dim customerList = From cust In customers
                   Group Join ord In orders On
                     cust.CustomerID Equals ord.CustomerID
                   Into CustomerOrders = Group,
                        TotalOfOrders = Sum(ord.Amount)
                   Select cust.CompanyName, cust.CustomerID,
                          CustomerOrders, TotalOfOrders

Aggregate 子句

需要 Aggregate 子句From 子句才能开始查询。 Aggregate 子句向集合应用一个或多个聚合函数。 例如,可以使用 Aggregate 子句计算查询所返回的所有元素之和,如以下示例所示。

' Returns the sum of all order amounts.
Dim orderTotal = Aggregate order In orders
                 Into Sum(order.Amount)

还可以使用 Aggregate 子句来修改查询。 例如,可以使用 Aggregate 子句,对相关查询集合执行计算。 例如:

' Returns the customer company name and largest 
' order amount for each customer.
Dim customerMax = From cust In customers
                  Aggregate order In cust.Orders
                  Into MaxOrder = Max(order.Amount)
                  Select cust.CompanyName, MaxOrder

Let 子句

可选。 Let 子句计算一个值,并将其分配到查询中的新变量。 例如:

' Returns a list of products with a calculation of
' a ten percent discount.
Dim discountedProducts = From prod In products
                         Let Discount = prod.UnitPrice * 0.1
                         Where Discount >= 50
                         Select prod.Name, prod.UnitPrice, Discount

Distinct 子句

可选。 Distinct 子句限制当前迭代变量的值,以在查询结果中消除重复值。 例如:

' Returns a list of cities with no duplicate entries.
Dim cities = From item In customers
             Select item.City
             Distinct

Skip 子句

可选。 Skip 子句绕过集合中指定数量的元素,然后返回剩余的元素。 例如:

' Returns a list of customers. The first 10 customers
' are ignored and the remaining customers are
' returned.
Dim customerList = From cust In customers
                   Skip 10

Skip While 子句

可选。 Skip While 子句绕过集合中指定条件为 true 的任何元素,然后返回剩余元素。 例如:

' Returns a list of customers. The query ignores all
' customers until the first customer for whom
' IsSubscriber returns false. That customer and all
' remaining customers are returned.
Dim customerList = From cust In customers
                   Skip While IsSubscriber(cust)

Take 子句

可选。 Take 子句从集合的开头返回指定数量的连续元素。 例如:

' Returns the first 10 customers.
Dim customerList = From cust In customers
                   Take 10

Take While 子句

可选。 A Take While 子句包括集合中指定条件为 true 的任何元素,并绕过剩余元素。 例如:

' Returns a list of customers. The query returns
' customers until the first customer for whom 
' HasOrders returns false. That customer and all 
' remaining customers are ignored.
Dim customersWithOrders = From cust In customers
                          Order By cust.Orders.Count Descending
                          Take While HasOrders(cust)

使用其他 LINQ 查询功能

可以通过调用 LINQ 提供的可枚举类型和可查询类型的成员来使用其他 LINQ 查询功能。 可以通过对查询表达式的结果调用特定查询运算符来使用这些附加功能。 例如,下面的示例使用 Enumerable.Union 方法,将两个查询的结果合并为一个查询结果。 它使用 Enumerable.ToList 方法,将查询结果返回为一个泛型列表。

Public Function GetAllCustomers() As List(Of Customer)
    Dim customers1 = From cust In domesticCustomers
    Dim customers2 = From cust In internationalCustomers

    Dim customerList = customers1.Union(customers2)

    Return customerList.ToList()
End Function

有关其他 LINQ 功能的详细信息,请参阅标准查询运算符概述

使用 LINQ to SQL 连接到数据库

在 Visual Basic 中,可使用 LINQ to SQL 文件标识希望访问的 SQL Server 数据库对象(如表、视图和存储过程)。 LINQ to SQL 文件具有 .dbml 扩展名。

有效地连接到 SQL Server 数据库时,即可向项目添加“LINQ to SQL 类”项模板。 此操作将显示对象关系设计器(O/R 设计器)。 使用 O/R 设计器,可以将代码中希望访问的项从“服务器资源管理器”/“数据库资源管理器”拖动到设计器图面上。 LINQ to SQL 文件可向项目添加 DataContext 对象。 此对象包括你希望访问的表和视图的属性和集合,以及希望调用的存储过程的方法。 保存对 LINQ to SQL (.dbml) 文件所作的更改后,即可通过引用由 O/R 设计器定义的 DataContext 对象,在代码中访问这些对象。 项目的 DataContext 对象根据 LINQ to SQL 文件的名称进行命名。 例如,名为 Northwind.dbml 的 LINQ to SQL 文件将创建名为 NorthwindDataContextDataContext 对象。

有关分步说明的示例,请参阅如何:查询数据库如何:调用存储过程

支持 LINQ 的 Visual Basic 功能

Visual Basic 还包括其他值得注意的功能,它们能够简化 LINQ 的使用,并减少执行 LINQ 查询所必须编写的代码量。 其中包括:

  • 匿名类型,可用于根据查询结果创建新类型。

  • 隐式类型化变量,可用于延迟指定类型,并让编译器根据查询结果推断类型。

  • 扩展方法,可用于使用你自己的方法扩展现有类型而不修改类型本身。

有关详细信息,请参阅支持 LINQ 的 Visual Basic 功能

延迟执行和立即执行查询

执行查询与创建查询相互独立。 创建查询后,通过单独的机制触发其执行。 可以在定义查询后立即执行查询(立即执行),或者可以先存储定义,稍后再执行查询(延迟执行)。

默认情况下,创建查询后,它本身不会立即执行。 而会将查询定义存储在用于引用查询结果的变量中。 当稍后在代码中(如在 For…Next 循环中)访问查询结果变量时,将执行该查询。 此过程称为“延迟执行”。

也可在定义查询后立即执行查询,这即是“立即执行”。 可以通过应用需要访问查询结果中单个元素的方法来触发立即执行。 包括聚合函数(如 CountSumAverageMinMax)可产生此结果。 有关聚合函数的详细信息,请参阅 Aggregate 子句

使用 ToListToArray 方法也会强制立即执行。 当你希望立即执行查询并缓存查询结果时,这将很有用。 有关这些方法的详细信息,请参阅转换数据类型

有关查询执行的详细信息,请参阅编写你的第一个 LINQ 查询

Visual Basic 中的 XML

Visual Basic 中的 XML 功能包括 XML 文本和 XML 轴属性,可用于在代码中轻松创建、访问、查询和修改 XML。 XML 文本可用于在代码中直接编写 XML。 Visual Basic 编译器将 XML 视为第一类数据对象。

下面的代码示例演示了如何创建 XML 元素、访问其子元素和属性,以及使用 LINQ 查询该元素的内容。

' Place Imports statements at the top of your program.
Imports <xmlns:ns="http://SomeNamespace">

Module Sample1

    Sub SampleTransform()

        ' Create test by using a global XML namespace prefix.

        Dim contact =
            <ns:contact>
                <ns:name>Patrick Hines</ns:name>
                <ns:phone ns:type="home">206-555-0144</ns:phone>
                <ns:phone ns:type="work">425-555-0145</ns:phone>
            </ns:contact>

        Dim phoneTypes =
          <phoneTypes>
              <%= From phone In contact.<ns:phone>
                  Select <type><%= phone.@ns:type %></type>
              %>
          </phoneTypes>

        Console.WriteLine(phoneTypes)
    End Sub

End Module

有关更多信息,请参见 XML

主题 说明
XML 描述 Visual Basic 中可查询和可将 XML 包括为 Visual Basic 代码中的第一类数据对象的 XML 功能。
查询 提供有关 Visual Basic 中可用查询子句的参考信息。
LINQ(语言集成查询) 包括常规信息、编程指南和 LINQ 示例。
LINQ to SQL 包括常规信息、编程指南和 LINQ to SQL 示例。
LINQ to Objects 包括常规信息、编程指南和 LINQ to Objects 示例。
LINQ to ADO.NET(门户网站页) 包括常规信息、编程指南和 LINQ to ADO.NET 示例。
LINQ to XML 包括常规信息、编程指南和 LINQ to XML 示例。

“操作说明”和“演练”主题

如何:查询数据库

如何:调用存储过程

如何:修改数据库中的数据

如何:使用联接合并数据

如何:对查询结果进行排序

如何:筛选查询结果

如何:对数据进行计数、求和与求平均值计算

如何:查找查询结果中的最小值或最大值

如何:分配存储流程来执行更新、插入和删除操作(O/R 设计器)

第 17 章:LINQVisual Basic 2008 编程

另请参阅