1.语言集成查询
语言集成查询 (LINQ) 是一系列直接将查询功能集成到 C# 语言的技术统称。 数据查询历来都表示为简单的字符串,没有编译时类型检查或 IntelliSense 支持。 此外,需要针对每种类型的数据源了解不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等。 借助 LINQ,查询成为了最高级的语言构造,就像类、方法和事件一样。
对于编写查询的开发者来说,LINQ 最明显的“语言集成”部分就是查询表达式。 查询表达式采用声明性 查询语法 编写而成。 使用查询语法,可以用最少的代码对数据源执行筛选、排序和分组操作。 可使用相同的基本查询表达式模式来查询和转换 SQL 数据库、ADO .NET 数据集、XML 文档和流以及 .NET 集合中的数据。
class LINQQueryExpressions
{
static void Main()
{
// Specify the data source.
int[] scores = new int[] { 97, 92, 81, 60 };
// Define the query expression.
IEnumerable<int> scoreQuery =
from score in scores
where score > 80
select score;
// Execute the query.
foreach (int i in scoreQuery)
{
Console.Write(i + " ");
}
}
}
2.查询表达式概述
-
查询表达式可用于查询并转换所有启用了 LINQ 的数据源中的数据。 例如,通过一个查询即可检索 SQL 数据库中的数据,并生成 XML 流作为输出。
-
查询表达式易于掌握,因为使用了许多熟悉的 C# 语言构造。
-
查询表达式中的变量全都是强类型,尽管在许多情况下,无需显式提供类型,因为编译器可以推断出。 有关详细信息,请参阅 LINQ 查询操作中的类型关系。
-
只有在循环访问查询变量后,才会执行查询(例如,在
foreach
语句中)。 有关详细信息,请参阅 LINQ 查询简介。 -
在编译时,查询表达式根据 C# 规范规则转换成标准查询运算符方法调用。 可使用查询语法表示的任何查询都可以使用方法语法进行表示。 不过,在大多数情况下,查询语法的可读性更高,也更为简洁。 有关详细信息,请参阅 C# 语言规范和标准查询运算符概述。
-
通常,我们建议在编写 LINQ 查询时尽量使用查询语法,并在必要时尽可能使用方法语法。 这两种不同的形式在语义或性能上毫无差异。 查询表达式通常比使用方法语法编写的等同表达式更具可读性。
-
一些查询操作(如 Count 或 Max)没有等效的查询表达式子句,因此必须表示为方法调用。 可以各种方式结合使用方法语法和查询语法。 有关详细信息,请参阅 LINQ 中的查询语法和方法语法。
-
查询表达式可被编译成表达式树或委托,具体视应用查询的类型而定。 IEnumerable<T> 查询编译为委托。 IQueryable 和 IQueryable<T> 查询编译为表达式树。 有关详细信息,请参阅表达式树。
3.查询表达式基础
1.查询是什么及其作用是什么?
查询是一组指令,描述要从给定数据源(或源)检索的数据以及返回的数据应具有的形状和组织。 查询与它生成的结果不同。
通常情况下,源数据按逻辑方式组织为相同类型的元素的序列。 例如,SQL 数据库表包含行的序列。 在 XML 文件中,存在 XML 元素的“序列”(尽管这些元素在树结构按层次结构进行组织)。 内存中集合包含对象的序列。
从应用程序的角度来看,原始源数据的特定类型和结构并不重要。 应用程序始终将源数据视为 IEnumerable<T> 或 IQueryable<T> 集合。 例如在 LINQ to XML 中,源数据显示为 IEnumerable
<XElement>。
对于此源序列,查询可能会执行三种操作之一:
-
检索元素的子集以生成新序列,而不修改各个元素。 查询然后可能以各种方式对返回的序列进行排序或分组,如下面的示例所示(假定
scores
是int[]
):
IEnumerable<int> highScoresQuery =
from score in scores
where score > 80
orderby score descending
select score;
检索有关源数据的单独值,如:
-
与特定条件匹配的元素数。
-
具有最大或最小值的元素。
-
与某个条件匹配的第一个元素,或指定元素集中特定值的总和。 例如,下面的查询从
scores
整数数组返回大于 80 的分数的数量:
int highScoreCount =
(from score in scores
where score > 80
select score)
.Count();
在前面的示例中,请注意在调用 Count
方法之前,在查询表达式两边使用了括号。 也可以通过使用新变量存储具体结果,来表示此行为。 这种方法更具可读性,因为它使存储查询的变量与存储结果的查询分开。
2.查询表达式是什么?
查询表达式是以查询语法表示的查询。 查询表达式是一流的语言构造。 它如同任何其他表达式一样,可以在 C# 表达式有效的任何上下文中使用。 查询表达式由一组用类似于 SQL 或 XQuery 的声明性语法所编写的子句组成。 每个子句进而包含一个或多个 C# 表达式,而这些表达式可能本身是查询表达式或包含查询表达式。
查询表达式必须以 from 子句开头,且必须以 select 或 group 子句结尾。 在第一个 from
子句与最后一个 select
或 group
子句之间,可以包含以下这些可选子句中的一个或多个:where、orderby、join、let,甚至是其他 from 子句。 还可以使用 into 关键字,使 join
或 group
子句的结果可以充当相同查询表达式中的其他查询子句的源。
3.查询变量
在 LINQ 中,查询变量是存储查询而不是查询结果的任何变量。 更具体地说,查询变量始终是可枚举类型,在 foreach
语句或对其 IEnumerator.MoveNext
方法的直接调用中循环访问时会生成元素序列。
下面的代码示例演示一个简单查询表达式,它具有一个数据源、一个筛选子句、一个排序子句并且不转换源元素。 该查询以 select
子句结尾。
static void Main()
{
// Data source.
int[] scores = { 90, 71, 82, 93, 75, 82 };
// Query Expression.
IEnumerable<int> scoreQuery = //query variable
from score in scores //required
where score > 80 // optional
orderby score descending // optional
select score; //must end with select or group
// Execute the query to produce the results
foreach (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
}
4.开始查询表达式
查询表达式必须以 from
子句开头。 它指定数据源以及范围变量。 范围变量表示遍历源序列时,源序列中的每个连续元素。 范围变量基于数据源中元素的类型进行强类型化。 在下面的示例中,因为 countries
是 Country
对象的数组,所以范围变量也类型化为 Country
。 因为范围变量是强类型,所以可以使用点运算符访问该类型的任何可用成员。
Enumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
select country;
5.结束查询表达式
查询表达式必须以 group
子句或 select
子句结尾。
(1).group 子句
使用 group
子句可生成按指定键组织的组的序列。 键可以是任何数据类型。 例如,以下查询会创建包含一个或多个 Country
对象,并且其关键值是数值为国家/地区名称首字母的 char
类型。
ar queryCountryGroups =
from country in countries
group country by country.Name[0]
(2).select 子句
使用 select
子句可生成所有其他类型的序列。 简单 select
子句只生成类型与数据源中包含的对象相同的对象的序列。 在此示例中,数据源包含 Country
对象。 orderby
子句只按新顺序对元素进行排序,而 select
子句生成重新排序的 Country
对象的序列。
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
elect
子句可以用于将源数据转换为新类型的序列。 此转换也称为投影。 在下面的示例中,select
子句对只包含原始元素中的字段子集的匿名类型序列进行投影。 请注意,新对象使用对象初始值设定项进行初始化。
6.where 子句
使用 where
子句可基于一个或多个谓词表达式,从源数据中筛选出元素。 以下示例中的 where
子句具有一个谓词及两个条件。
Enumerable<City> queryCityPop =
from city in cities
where city.Population < 200000 && city.Population > 100000
select city;