Linq学习第二天

查询表达式基础

通常,源数据会在逻辑上组织为相同种类的元素序列。 SQL 数据库表包含一个行序列。 与此类似,ADO.NET DataTable 包含一个DataRow对象序列。 在 XML 文件中,有一个 XML 元素“序列”(不过这些元素按分层形式组织为树结构)。 内存中的集合包含一个对象序列。应用程序始终将源数据视为一个 IEnumerable(Of T)或IQueryable(Of T)集合。 在 LINQ to XML 中,源数据显示为一个 IEnumerable<XElement >。 在 LINQ to DataSet 中,它是一个 IEnumerable<DataRow >。 在 LINQ to SQL 中,它是您定义用来表示 SQL 表中数据的任何自定义对象的 IEnumerableIQueryable

指定此源序列后,查询可以进行下列三项工作之一: 检索一个元素子集以产生一个新序列,但不修改单个元素。 然后,查询可以按各种方式对返回的序列进行排序或分组,如下面的示例所示(假定 scores 是 int[]):

1 IEnumerable<int> highScoresQuery =
2     from score in scores
3     where score > 80
4     orderby score descending
5 select score;

如上一个示例所述检索一个元素序列,但是将这些元素转换为具有新类型的对象。 例如,查询可以只从数据源中的某些客户记录检索姓氏。 或者,查询可以检索完整的记录,再使用它构建另一个内存中对象类型甚至 XML 数据,然后生成最终的结果序列。 下面的示例演示了从 intstring的转换。 请注意 highScoresQuery的新类型。

1 IEnumerable<string> highScoresQuery2 =
2     from score in scores
3     where score > 80
4     orderby score descending
5     select String.Format("The score is {0}", score);

检索有关源数据的单一值,例如:

符合某个条件的元素的数量。

具有最大值或最小值的元素。

符合某个条件的第一个元素,或一组指定元素中的特定值之和。 例如,下面的查询从 scores整数数组中返回高于 80 的分数的数量。

1 int highScoreCount =
2     (from score in scores
3      where score > 80
4      select score)
5      .Count();

在上一个示例中,请注意在 Count 方法调用之前的查询表达式两旁使用了括号。 另一种表示方式是使用一个新变量来存储具体结果。 此技术的可读性更好,因为它将存储查询的变量与存储结果的查询区分开来。

1 IEnumerable<int> highScoresQuery3 =
2     from score in scores
3     where score > 80
4     select score;

查询表达式

查询表达式是用查询语法表示的查询, 是一流的语言构造。 它就像任何其他表达式一样,并且可以用在 C# 表达式有效的任何上下文中。 查询表达式由一组用类似于 SQL XQuery 的声明性语法编写的子句组成。 每个子句又包含一个或多个 C# 表达式,而这些表达式本身又可能是查询表达式或包含查询表达式。

查询表达式必须以 from 子句开头,并且必须以 selectgroup子句结尾。 在第一个 from子句和最后一个 selectgroup子句之间,查询表达式可以包含一个或多个下列可选子句:whereorderbyjoinlet甚至附加的 from子句。 还可以使用 into关键字使 joingroup子句的结果能够充当同一查询表达式中附加查询子句的源。

 

查询变量的显式类型化和隐式类型化

本文档通常提供查询变量的显式类型,以便演示查询变量和 select 子句之间的类型关系。 但是,也可以使用 var 关键字指示编译器在编译时推断查询变量(或任何其他本地变量)的类型。 例如,还可以使用隐式类型化表示本主题前面部分中演示的查询示例: 

1 // Use of var is optional here and in all queries.
2 // queryCities is an IEnumerable<City> just as 
3 // when it is explicitly typed.
4 var queryCities =
5     from city in cities
6     where city.Population > 100000
7     select city;


 

 开始查询表达式

查询表达式必须以 from子句开头。 它同时指定了数据源和范围变量。 在对源序列进行遍历的过程中,范围变量表示源序列中的每个后续元素。 将根据数据源中元素的类型对范围变量进行强类型化。 在下面的示例中,因为 countriesCountry对象数组,所以范围变量也被类型化为 Country 这样就可以使用点运算符来访问该类型的任何可用成员。

1 IEnumerable<Country> countryAreaQuery =
2     from country in countries
3     where country.Area > 500000 //sq km
4     select country;

 

 在使用分号或延续子句退出查询之前,范围变量将一直位于范围中。

查询表达式可以包含多个 from子句。 当源序列中的每个元素本身就是集合或包含集合时,可使用附加的 from子句。 例如,假定您具有一个 Country对象集合,而其中每个对象都包含一个名为 CitiesCity对象集合。 若要查询每个 Country中的 City对象,请使用两个from子句,如下所示:

1 IEnumerable<City> cityQuery =
2     from country in countries
3     from city in country.Cities
4     where city.Population > 10000
5     select city;

 

 

group 子句

使用 group子句可产生按照指定的键组织的组序列。 键可以采用任何数据类型。 例如,下面的查询创建一个组序列,该序列包含一个或多个 Country对象,并且它的键是 char值。

1 var queryCountryGroups =
2     from country in countries
3     group country by country.Name[0];

select 子句

使用 select子句可产生所有其他类型的序列。 简单的 select子句只是产生与数据源中包含的对象具有相同类型的对象的序列。 在此示例中,数据源包含 Country对象。 orderby子句只是将元素重新排序,而 select子句则产生重新排序的 Country对象的序列。

1 IEnumerable<Country> sortedQuery =
2     from country in countries
3     orderby country.Area
4     select country;

使用“into”进行延续

可以在 selectgroup子句中使用 into关键字来创建用于存储查询的临时标识符。 当您必须在分组或选择操作之后对查询执行附加查询操作时,需要这样做。 在下面的示例中,以一千万人口范围为界对 countries进行分组。 在创建这些组之后,使用附加子句筛选掉某些组,然后按升序对剩下的组进行排序。 若要执行这些附加操作,需要使用由 countryGroup表示的延续。  

 

 1 // percentileQuery is an IEnumerable<IGrouping<int, Country>>
 2 var percentileQuery =
 3     from country in countries
 4     let percentile = (int) country.Population / 10000000
 5     group country by percentile into countryGroup
 6     where countryGroup.Key >= 20
 7     orderby countryGroup.Key
 8     select countryGroup;
 9 
10 // grouping is an IGrouping<int, Country>
11 foreach (var grouping in percentileQuery)
12 {
13     Console.WriteLine(grouping.Key);
14     foreach (var country in grouping)
15         Console.WriteLine(country.Name + ":" + country.Population);
16 }

筛选、排序和联接

from开始子句以及 selectgroup结束子句之间,所有其他子句(wherejoinorderbyfromlet)都是可选的。 任何可选子句都可以在查询正文中使用零次或多次。 

where 子句

使用 where子句可以根据一个或多个谓词表达式筛选掉源数据中的某些元素。 以下示例中的 where子句含有两个谓词。

1 IEnumerable<City> queryCityPop =
2     from city in cities
3     where city.Population < 200000 && city.Population > 100000
4     select city;

orderby 子句

使用 orderby子句可以按升序或降序对结果进行排序。 您还可以指定次要排序顺序。 下面的示例使用 Area属性对 country对象执行主要排序, 然后使用 Population属性执行次要排序。

1 IEnumerable<Country> querySortedCountries =
2     from country in countries
3     orderby country.Area, country.Population descending
4     select country;

join 子句

使用 join子句可以根据每个元素中指定键之间的相等比较,对一个数据源中的元素与另外一个数据源中的元素进行关联和/或组合。 LINQ 中,联接操作是针对其元素具有不同类型的对象序列执行的。 在联接两个序列之后,必须使用 selectgroup语句指定要存储到输出序列中的元素。 还可以使用匿名类型将每组关联元素中的属性组合为输出序列的新类型。 下面的示例对其 Category属性与 categories字符串数组中的某个类别相匹配的 prod对象进行关联。 Category不与 categories中的任何字符串匹配的产品会被筛选掉。 select语句投影了一个新类型,其属性取自 catprod

1 var categoryQuery =
2     from cat in categories
3     join prod in products on cat equals prod.Category
4     select new { Category = cat, Name = prod.Name };

 let 子句

使用 let子句可以将表达式(如方法调用)的结果存储到新的范围变量中。 在下面的示例中,范围变量 firstName存储了 Split返回的字符串数组的第一个元素。

1 string[] names = { "Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia" };
2 IEnumerable<string> queryFirstNames =
3     from name in names
4     let firstName = name.Split(new char[] { ' ' })[0]
5     select firstName;
6 
7 foreach (string s in queryFirstNames)
8     Console.Write(s + " ");
9 //Output: Svetlana Claire Sven Cesar

查询表达式中的子查询

查询子句本身可能包含一个查询表达式,该查询表达式有时称为子查询 每个子查询都以它自己的 from子句开头,该子句不一定指向第一个 from子句中的同一数据源。 例如,下面的查询演示了一个在 select 语句中使用的查询表达式,用来检索分组操作的结果。

 1 var queryGroupMax =
 2     from student in students
 3     group student by student.GradeLevel into studentGroup
 4     select new
 5     {
 6         Level = studentGroup.Key,
 7         HighestScore =
 8             (from student2 in studentGroup
 9              select student2.Scores.Average())
10              .Max()
11     };

 

 

 

 

posted @ 2012-04-13 09:57  孙金棚  阅读(1178)  评论(0编辑  收藏  举报