LINQ 语法
语言集成查询 (LINQ) 是 Visual Studio 2008 和 .NET Framework 3.5 版中一项突破性的创新,它在对象领域和数据领域之间架起了一座桥梁。
传统上,针对数据的查询都是以简单的字符串表示,而没有编译时类型检查或 IntelliSense 支持。此外,您还必须针对以下各种数据源学习不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等。LINQ 使查询成为 C# 和 Visual Basic 中的一等语言构造。您可以使用语言关键字和熟悉的运算符针对强类型化对象集合编写查询。下图显示了一个用 C# 语言编写的、不完整的 LINQ 查询,该查询针对 SQL Server 数据库,并具有完全类型检查和 IntelliSense 支持。
一、查询的三个阶段
LINQ 查询操作由以下三个操作组成:
1、获取数据源:支持泛型 IEnumerable(Of (T)) 接口的所有对象都可以作为LINQ的数据源,包括Array、List等。
2、创建查询:指定要从数据源中检索的哪些信息,还可以指定在返回这些信息之前如何对其进行排序、分组或结构化。创建(定义)的查询通常存储在变量中,并不会在定义时马上执行而是在以后执行。所以记住:查询变量本身不会执行任何操作,也不会返回任何数据,它只是存储查询定义。
3、执行查询:执行查询有两种情况,1是延迟执行;2是立即执行。
3.1、延迟执行:查询定义并保存在查询定义变量后,不马上执行,而是在后续需要时才执行。这通常需要使用 For Each 循环(它返回值序列)或应用标准查询运算符。对于值序列,可使用 For Each 循环中的迭代变量(在上一个示例中为 number)访问所检索的数据。
3.2、立即执行:在立即执行过程中,查询在定义时执行。当应用需要访问查询结果的各个元素的方法时,就会触发执行。通常使用能够返回单个值的标准查询运算符之一来强制立即执行。Count、Max、Average 和 First 就属于标准查询运算符。只要应用了这些标准查询运算符以便计算并返回单一实例结果,这些运算符就会立即执行查询。例如:
Dim numbers中的偶数个数 = (From num In numbers _
Where num Mod 2 = 0 _
Select num).Count()
还可通过对查询(立即执行)或查询变量(延迟执行)调用 ToList 或 ToArray 方法来强制执行查询,如下面的代码所示:
Dim evensList = (From num In numbers _
Where num Mod 2 = 0 _
Select num).ToList()
二、查询的基本操作
1、制定数据源(FROM):
在 LINQ 查询中,第一步是指定要查询的数据源。因此,查询中的 From 子句总是最先出现。语法是:FROM 范围变量 in 数据源 ,范围变量类似于循环迭代变量,但在查询表达式中,实际上不发生迭代。执行查询(通常使用 For Each 循环执行)时,范围变量将用作对 数据源集合中的每个元素的引用。From 子句用于标识查询的源数据,以及用于引用源集合中元素的变量。这些变量称为范围变量。可以指定查询中的多个 From 子句,以标识要联接的多个集合。指定多个集合时,将单独循环访问这些集合,或者如果这些集合是相关的,则可以联接它们。您可以通过使用 Select 子句隐式联接集合,或者通过使用 Join 或 Group Join 子句显式联接集合。或者,您可以在单个 From 子句中指定多个范围变量和集合,并用逗号将每个相关的范围变量和集合分隔开。每个 From 子句后面可跟其他查询子句的任意组合以优化查询。
1.1:完整的语法:
From 范围变量1 [ As 类型 ] In 集合 [ _ ]
[, 范围变量2 [ As 类型2 ] In 集合2 [, ... ] ]
范围变量,用于在查询循环访问 集合 时,引用 集合 的每个成员。必须为可枚举类型。
范围变量的类型可选。如果不指定 类型,则编译器自动根据集合推断范围变量的类型。后续 From 子句可以引用 From 子句中的范围变量,或者它们可以引用以前的 From 子句中的范围变量。
范例:
Dim allOrders = From cust In GetCustomerList() _
From ord In cust.Orders _
Select ord
2、筛选数据(WHERE):
Where 子句用于执行筛选。筛选器指定要在结果序列中包含数据源中的哪些元素,可以使用逻辑运算符(如 And、Or、AndAlso、OrElse、Is 和 IsNot)可以将多个表达式组合在一个 Where 子句中。
默认情况下,查询表达式直到被访问时才计算结果。因此,在访问查询前,不会计算 Where 子句。所以如果值位于 Where 子句中使用的查询外部,请确保查询执行时在 Where 子句中使用适当的值。
您可以在 Where 子句中调用函数,以便对集合中当前元素的值执行计算或运算。在 Where 子句中调用函数可使查询在定义后立即执行,而不是在访问时执行。
3、 对数据排序(ORDER BY):
Order By 子句将使所返回序列中的元素按指定的一个或多个字段排序。若要按相反顺序(从 Z 到 A)对结果排序,请使用 Order By...Descending 子句。如果 Ascending 和 Descending 都未指定,则默认为 Ascending。
注意:如果要按照没有用于 Select 子句的字段对结果进行排序,则必须将 Order By 子句放在 Select 子句之前。
4、选择数据(SELECT):
Select 子句指定所返回元素的形式和内容。若要检索包含整个数据源对象的集合,请选择范围变量本身。若要从数据源中选择多个字段,您可以使用两种方法:
4.1、在 Select 子句中,指定要包含在结果中的字段。编译器将定义一个匿名类型,该类型将这些字段作为其属性。如:
Dim londonCusts4 = From cust In customers _
Where cust.City = "London" _
Order By cust.Name Ascending _
Select Name = cust.Name, Phone = cust.Phone
4.2、定义含有您要包括在结果中的特定字段的命名类型,并在 Select 子句中创建和初始化该类型的实例。仅当您必须在返回各个结果的集合以外使用这些结果,或者必须将这些结果作为参数传入方法调用时,才使用此选项。
Dim londonCusts5 = From cust In customers _
Select New NamePhone With {.Name = cust.Name, _
.Phone = cust.Phone}
如果未指定 Select 子句,查询将根据为当前范围变量的所有成员返回一个类型。
Select 子句可以引用当前范围中的任何变量,包括在 From 子句中标识的范围变量,由 Aggregate、Let、Group By 或 Group Join 子句通过别名创建的任何新变量或查询表达式中之前的 Select 子句创建的变量。Select 子句还可以包含静态值。
Select 子句可为后续的其他子句子句引入一组新的范围变量,而之前的范围变量不再位于范围中,查询表达式中的最后一个 Select 子句确定查询的返回值。例如:
Dim customerList = From cust In customers, ord In cust.Orders _
Select Name = cust.CompanyName, _ '引入新范围变量
Total = ord.Total, ord.OrderID _ '引入新范围变量
Where Total > 500 _ '后续子句使用新的范围比例
Select Name, OrderID
5、联接数据(JOIN 或 GROUP JOIN):
您可以使用多种方法将多个数据源组合到 From 子句中。Join 关键字等效于 SQL 中的 INNER JOIN。它基于两个集合中的元素之间的匹配键值对这两个集合进行组合。例如:以下查询姓氏以元音开头的学生:
Dim vowels() As String = {"A", "E", "I", "O", "U"}
Dim vowelNames = From student In students, vowel In vowels _
Where student.Last.IndexOf(vowel) = 0 _
Select Name = student.First & " " & _
student.Last, Initial = vowel _
Order By Initial
或者:
Dim vowelNames2 = From student In students _
Join vowel In vowels _
On student.Last(0) Equals vowel _
Select Name = student.First & " " & _
student.Last, Initial = vowel _
Order By Initial
GROUP JOIN相当于SQL中的LEFT OUTER JOIN,将多个集合组合为单个分层集合。
可以在查询中使用多个 Join 子句,以便将两个或更多集合联接为单个集合。
在不使用 Join 子句的情况下,可以执行隐式联接来组合集合。为此,应在 From 子句中包括多个 In 子句,并指定标识要用于联接的键的 Where 子句。
完整语法:
Group Join element [As type] In collection _
On key1 Equals key2 [ And key3 Equals key4 [... ] ] _
Into expressionList
其中,expressionList是必须的,是一个或多个表达式,标识对集合中的元素组进行聚合的方式。若要为分组结果标识一个成员名称,可使用 Group 关键字 (<alias> = Group)。
Group Join 运算将返回 Group Join 运算符左侧标识的集合中的所有结果。即使要联接的集合中没有匹配项,也是如此。这与 SQL 中的 LEFT OUTER JOIN 类似。
6、对数据进行分组(GROUP BY):
根据元素的一个或多个字段对查询结果中的元素进行分组。例如:按年级 (class year) 对学生进行分组:
Dim studentsByYear = From student In students _
Select student _
Group By year = student.Year _
Into Classes = Group
输出结果的程序:
For Each yearGroup In studentsByYear
Console.WriteLine(vbCrLf & "Year: " & yearGroup.year)
For Each student In yearGroup.Classes
Console.WriteLine(" " & student.Last & ", " & student.First)
Next
Next
完整语法:
Group [ listField1 [, listField2 [...] ] By keyExp1 [, keyExp2 [...] ]
Into aggregateList
listField1, listField2 :可选。查询变量的一个或多个字段,这些查询变量显式标识要包括在分组结果中的字段。如果未指定任何字段,则查询变量的所有字段都包括在分组结果中。
keyExp1 :必需。一个表达式,标识用于确定元素的分组的键。可以指定多个键来指定一个组合键。
keyExp2 :可选。一个或多个附加键,与 keyExp1 组合在一起,创建一个组合键。
aggregateList :必需。一个或多个表达式,标识如何对组进行聚合。若要为分组结果标识一个成员名称,请使用 Group 关键字,该关键字可以:Into Group
例如:
Dim 第一层_分组 = From cust In db.Customers _
Group By 国家 = cust.Country _
Into 第二层_分组元素 = Group, Count() _
Order By 国家
For Each A分组 In 第一层_分组
Console.WriteLine(A分组.国家 & "(" & A分组.Count & ")")
For Each A元素 In A分组.第二层_分组元素
Console.WriteLine(vbTab + A元素.CompanyName + "," + A元素.ContactName)
Next
Next
[
笔记完毕,原来LINQ的功能非常强大,可是也因为它强大,所以很不好理解,特别是很容易用以往的SQL的知识来理解LINQ,结果不得入门。这两天认认真真地阅读了MSDN中关于LINQ的很多文章,在VS2008中进行了实战验证,才基本搞懂了LINQ各子句的功能,理解了他的原理,感觉很新鲜。今后,还要在实践中不断巩固对LINQ的理解,尽快将LINQ应用的实际开发中来。
终于搞清楚了!
在.Net 3.5 中,加入了多项新的特性,其中 LINQ 是我最感兴趣的新特性之一。
长时间从事J2EE开发,对Hibernate、Spring等已经比较熟悉,而.Net中新加入的LINQ缺更让人觉得解决ORM上更彻底。它实现了ORM的同时,让数据模型与开发IDE完全融合,可以充分利用IDE的数据感知功能,实现在对数据访问编程时,可以在编辑时进行语法检查和校验,大大降低程序编码的错误,有效提高程序质量。
而且,在VisualStudio 2008上,LINQ得到非常好的支持,不但容易学习掌握,而且使用起来及其方便简洁,理解了之后,相信很多.Net程序员会喜欢上LINQ。
LINQ to SQL的用法步骤基本是:
查询:
dim 数据库 as DataClasses1DataContext '定义数据库
dim 查询变量名 = From 全部记录 In 数据库.表或者视图名 Select 全部记录
For Each 一笔记录 In 查询变量名
result = result + 一笔记录.字段名 + ","
Next
插入记录:
Dim 新的记录 As New student
新的记录.name = "新学生"
新的记录.age = 24
db.student.InsertOnSubmit(新的记录)
就这么简单!
LINQ与WCF或者Windows服务应用结合,可以发挥更大的作用。