查询是什么及其作用是什么

查询是一组指令,描述要从给定数据源(或源)检索的数据以及返回的数据应具有的形状和组织。 查询与它生成的结果不同。

通常情况下,源数据按逻辑方式组织为相同类型的元素的序列。 例如,SQL 数据库表包含行的序列。 在 XML 文件中,存在 XML 元素的“序列”(尽管这些元素在树结构按层次结构进行组织)。 内存中集合包含对象的序列。

从应用程序的角度来看,原始源数据的特定类型和结构并不重要。 应用程序始终将源数据视为 IEnumerable<T> 或 IQueryable<T> 集合。 例如在 LINQ to XML 中,源数据显示为 IEnumerable<XElement>。

对于此源序列,查询可能会执行三种操作之一:

  • 检索元素的子集以生成新序列,而不修改各个元素。 查询然后可能以各种方式对返回的序列进行排序或分组,如下面的示例所示(假定 scores 是 int[]):

IEnumerable<int> highScoresQuery =
from score in scores
where score >
orderby score descending
select score;
  • 如前面的示例所示检索元素的序列,但是将它们转换为新类型的对象。 例如,查询可以只从数据源中的某些客户记录检索姓氏。 或者可以检索完整记录,然后用于构造其他内存中对象类型甚至是 XML 数据,再生成最终的结果序列。 下面的示例演示从 int 到 string 的投影。 请注意 highScoresQuery 的新类型。
IEnumerable<string> highScoresQuery2 =
from score in scores
where score >
orderby score descending
select $"The score is {score}";
  • 检索有关源数据的单独值,如:

    • 与特定条件匹配的元素数。

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

    • 与某个条件匹配的第一个元素,或指定元素集中特定值的总和。 例如,下面的查询从 scores 整数数组返回大于 80 的分数的数量:

int highScoreCount =
(from score in scores
where score >
select score)
.Count();

在前面的示例中,请注意在调用 Count 方法之前,在查询表达式两边使用了括号。也可以通过使用新变量存储具体结果,来表示此行为。 这种方法更具可读性,因为它使存储查询的变量与存储结果的查询分开。

IEnumerable<int> highScoresQuery3 =
from score in scores
where score >
select score; int scoreCount = highScoresQuery3.Count();

在上面的示例中,查询在 Count 调用中执行,因为 Count 必须循环访问结果才能确定 highScoresQuery 返回的元素数。

查询表达式是什么
  查询表达式是以查询语法表示的查询。 查询表达式是一流的语言构造。 它如同任何其他表达式一样,可以在 C# 表达式有效的任何上下文中使用。 查询表达式由一组用类似于 SQL 或 XQuery 的声明性语法所编写的子句组成。 每个子句进而包含一个或多个 C# 表达式,而这些表达式可能本身是查询表达式或包含查询表达式。
(1)查询表达式必须以 from 子句开头,且必须以 select 或 group 子句结尾。 
(2)在第一个 from 子句与最后一个 select 或 group 子句之间,可以包含以下这些可选子句中的一个或多个:whereorderbyjoinlet,甚至是其他 from 子句。 还可以使用 into 关键字,使 join 或 group 子句的结果可以充当相同查询表达式中的其他查询子句的源。

查询变量

在 LINQ 中,查询变量是存储查询而不是查询结果的任何变量。 更具体地说,查询变量始终是可枚举类型,在 foreach 语句或对其 IEnumerator.MoveNext 方法的直接调用中循环访问时会生成元素序列。

下面的代码示例演示一个简单查询表达式,它具有一个数据源、一个筛选子句、一个排序子句并且不转换源元素。 该查询以 select 子句结尾。

static void Main()
{
// 数据源
int[] scores = { , , , , , }; // 查询表达式
IEnumerable<int> scoreQuery = // 查询变量
from score in scores // 必须
where score > 80 // 可选
orderby score descending // 可选
select score; // 必须以 select 或者 group 结尾 // 执行查询并产生结果
foreach (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
}
// 输出: 93 90 82 82

在上面的示例中,scoreQuery 是查询变量,它有时仅仅称为查询。 查询变量不存储在 foreach 循环生成中的任何实际结果数据。 并且当 foreach 语句执行时,查询结果不会通过查询变量 scoreQuery 返回。 而是通过迭代变量 testScore 返回。 scoreQuery 变量可以在另一个 foreach 循环中进行循环访问。 只要既没有修改它,也没有修改数据源,便会生成相同结果。

查询变量可以存储采用查询语法、方法语法或是两者的组合进行表示的查询。 在以下示例中,queryMajorCities 和 queryMajorCities2 都是查询变量:

var cities = new City
{
new city(){Name = "上海",Population = },
new city(){Name = "南京",Population = },
new city(){Name = "北京",Population = },
new city(){Name = "广州",Population = }
}; // 查询语法
IEnumerable<City> queryMajorCities =
from city in cities
where city.Population >
select city; // 基于方法的语法
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > );

另一方面,以下两个示例演示不是查询变量的变量(即使各自使用查询进行初始化)。 它们不是查询变量,因为它们存储结果:

int highestScore =
(from score in scores
select score)
.Max(); // 或者拆分表达式
IEnumerable<int> scoreQuery =
from score in scores
select score; int highScore = scoreQuery.Max();
// 下面的表达式返回相同的结果
int highScore = scores.Max(); List<City> largeCitiesList =
(from country in countries
from city in country.Cities
where city.Population >
select city)
.ToList(); // 或者拆分表达式
IEnumerable<City> largeCitiesQuery =
from country in countries
from city in country.Cities
where city.Population >
select city; List<City> largeCitiesList2 = largeCitiesQuery.ToList();

有关表示查询的不同方式的详细信息,请参阅 LINQ 中的查询语法和方法语法

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

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

// 在这里和所有查询中使用var都是可选的。querycities是一个IEnumerable<city>就像它是显式类型一样
var queryCities =
from city in cities
where city.Population >
select city;

有关详细信息,请参阅隐式类型化局部变量和 LINQ 查询操作中的类型关系

开始查询表达式

查询表达式必须以 from 子句开头。 它指定数据源以及范围变量。 范围变量表示遍历源序列时,源序列中的每个连续元素。 范围变量基于数据源中元素的类型进行强类型化。 在下面的示例中,因为 countries 是 Country 对象的数组,所以范围变量也类型化为 Country。 因为范围变量是强类型,所以可以使用点运算符访问该类型的任何可用成员。

IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > //面积大于500000
select country;

范围变量一直处于范围中,直到查询使用分号或 continuation 子句退出。

查询表达式可能会包含多个 from 子句。 在源序列中的每个元素本身是集合或包含集合时,可使用其他 from 子句。 例如,假设具有 Country 对象的集合,其中每个对象都包含名为 Cities 的 City 对象集合。 若要查询每个 Country 中的 City 对象,请使用两个 from 子句,如下所示:

IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population >
select city;

有关详细信息,请参阅 from 子句

结束查询表达式

查询表达式必须以 group 子句或 select 子句结尾。

group 子句

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

var queryCountryGroups =
from country in countries
group country by country.Name[];

有关分组的详细信息,请参阅 group 子句

select 子句

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

IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;

select 子句可以用于将源数据转换为新类型的序列。 此转换也称为投影。 在下面的示例中,select 子句对只包含原始元素中的字段子集的匿名类型序列进行投影。 请注意,新对象使用对象初始值设定项进行初始化。

// 此处 var 是必须的,因为查询返回了匿名类型
var queryNameAndPop =
from country in countries
select new { Name = country.Name, Pop = country.Population };

有关可以使用 select 子句转换源数据的所有方法的详细信息,请参阅 select 子句

使用“into”进行延续

可以在 select 或 group 子句中使用 into 关键字创建存储查询的临时标识符。 如果在分组或选择操作之后必须对查询执行其他查询操作,则可以这样做。 在下面的示例中,countries 按 1000 万范围,根据人口进行分组。 创建这些组之后,附加子句会筛选出一些组,然后按升序对组进行排序。 若要执行这些附加操作,需要由 countryGroup 表示的延续。

// 该查询返回的类型是 IEnumerable<IGrouping<int, Country>>
var percentileQuery =
from country in countries
let percentile = (int) country.Population / 10_000_000
group country by percentile into countryGroup
where countryGroup.Key >=
orderby countryGroup.Key
select countryGroup; // 分组是 IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
Console.WriteLine(grouping.Key);
foreach (var country in grouping)
Console.WriteLine(country.Name + ":" + country.Population);
}

有关详细信息,请参阅 into

筛选、排序和联接

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

IEnumerable<City> queryCityPop =
from city in cities
where city.Population < && city.Population >
select city;

有关详细信息,请参阅 where 子句

orderby 子句

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

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

ascending 关键字是可选的;如果未指定任何顺序,则它是默认排序顺序。 有关详细信息,请参阅 orderby 子句

join 子句

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

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

还可以通过使用 into 关键字将 join 操作的结果存储到临时变量中来执行分组联接。 有关详细信息,请参阅 join 子句

let 子句

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

string[] names = { "Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia" };
IEnumerable<string> queryFirstNames =
from name in names
let firstName = name.Split(' ')[]
select firstName; foreach (string s in queryFirstNames)
Console.Write(s + " ");
//输出: Svetlana Claire Sven Cesar

有关详细信息,请参阅 let 子句

查询表达式中的子查询

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

var queryGroupMax =
from student in students
group student by student.GradeLevel into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore =
(from student2 in studentGroup
select student2.Scores.Average())
.Max()
};

有关详细信息,请参阅如何:对分组操作执行子查询

其他技术请参阅

C#3.0新增功能07 查询表达式的更多相关文章

  1. C#3.0新增功能08 Lambda 表达式

    连载目录    [已更新最新开发文章,点击查看详细] Lambda 表达式是作为对象处理的代码块(表达式或语句块). 它可作为参数传递给方法,也可通过方法调用返回. Lambda 表达式广泛用于: 将 ...

  2. C#2.0新增功能07 getter/setter 单独可访问性

    连载目录    [已更新最新开发文章,点击查看详细] 属性是一种成员,它提供灵活的机制来读取.写入或计算私有字段的值. 属性可用作公共数据成员,但它们实际上是称为访问器的特殊方法. 这使得可以轻松访问 ...

  3. C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点

    C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点   第一部分: C#是一种通用的,类型安全的,面向对象的编程语言.有如下特点: (1)面向对象:c# 是面向对象的范例的一个丰富实现, 它 ...

  4. C#2.0新增功能06 协变和逆变

    连载目录    [已更新最新开发文章,点击查看详细] 在 C# 中,协变和逆变能够实现数组类型.委托类型和泛型类型参数的隐式引用转换. 协变保留分配兼容性,逆变则与之相反. 以下代码演示分配兼容性.协 ...

  5. C#3.0新增功能09 LINQ 基础07 LINQ 中的查询语法和方法语法

    连载目录    [已更新最新开发文章,点击查看详细] 介绍性的语言集成查询 (LINQ) 文档中的大多数查询是使用 LINQ 声明性查询语法编写的.但是在编译代码时,查询语法必须转换为针对 .NET ...

  6. C#3.0新增功能09 LINQ 标准查询运算符 02 查询表达式语法

    连载目录    [已更新最新开发文章,点击查看详细] 某些使用更频繁的标准查询运算符具有专用的 C# 语言关键字语法,使用这些语法可以在查询表达式中调用这些运算符. 查询表达式是比基于方法的等效项更具 ...

  7. C#3.0新增功能09 LINQ 基础04 基本 LINQ 查询操作

    连载目录    [已更新最新开发文章,点击查看详细] 本篇介绍 LINQ 查询表达式和一些在查询中执行的典型操作. 获取数据源 在 LINQ 查询中,第一步是指定数据源. 和大多数编程语言相同,在使用 ...

  8. C#3.0新增功能09 LINQ 标准查询运算符 01 概述

    连载目录    [已更新最新开发文章,点击查看详细] 标准查询运算符 是组成 LINQ 模式的方法. 这些方法中的大多数都作用于序列:其中序列指其类型实现 IEnumerable<T> 接 ...

  9. C#3.0新增功能09 LINQ 标准查询运算符 04 运算

    连载目录    [已更新最新开发文章,点击查看详细] 本篇主要介绍标准查询运算符的常用运算功能. 01 对数据排序 排序操作基于一个或多个属性对序列的元素进行排序. 第一个排序条件对元素执行主要排序. ...

随机推荐

  1. 解决C/C++程序执行一闪而过的方法(使用getchar,或者cin.get,不推荐system(“pause”))

    简述 在VS编写控制台程序的时候,包括使用其他IDE(Visual C++)编写C/C++程序,经常会看到程序的执行结果一闪而过,要解决这个问题,可以在代码的最后加上system(“pause”).g ...

  2. myeclipse2018的下载安装教程

    首先注意事项!!!!!!! 在安装破解前是不可以打开软件的 jdk版本不能是10版本,1.8或1.9都可以 附上MyEclipse2018的百度云下载链接: 链接:https://pan.baidu. ...

  3. Mac上使用brew安装nvm来支持多版本的Nodejs

    brew方式 如果机器没有安装过node,那么首先brew install nvm安装nvm. 其次需要在shell的配置文件(~/.bashrc, ~/.profile, or ~/.zshrc)中 ...

  4. What?一个 Dubbo 服务启动要两个小时!

    前言 前几天在测试环境碰到一个非常奇怪的与 dubbo 相关的问题,事后我在网上搜索了一圈并没有发现类似的帖子或文章,于是便有了这篇. 希望对还未碰到或正在碰到的朋友有所帮助. 现象 现象是这样的,有 ...

  5. Mac上PyCharm运行多进程报错的解决方案

    Mac上PyCharm运行多进程报错的解决方案 运行时报错 may have been in progress in another thread when fork() was called. We ...

  6. 一文看懂Python的面向对象编程

    之前在网络上看了很多关于面向对象的编程详解,还是不够过瘾,所以决定自己动手写一篇. 面向对象:Object Oriented Programming,简称OOP,即面向对象程序设计. 类(Class) ...

  7. POJ 1741:Tree(树上点分治)

    题目链接 题意 给一棵边带权树,问两点之间的距离小于等于K的点对有多少个. 思路 <分治算法在树的路径问题中的应用> 图片转载于http://www.cnblogs.com/Paul-Gu ...

  8. C# 中的委托和事件本质讲解

    C# 中的委托和事件 文中代码在VS2005下通过,由于VS2003(.Net Framework 1.1)不支持隐式的委托变量,所以如果在一个接受委托类型的位置直接赋予方法名,在VS2003下会报错 ...

  9. Oracle数据库常用脚本命令(二)

    --创建学生信息表create table student( sid number(8,0), name varchar2(20), sex char(2), birthday date, addre ...

  10. asp.net core 系列之Configuration

    在ASP.NET Core中的App configuration 是通过configuration providers基于key-value对建立的.Configuration providers读取 ...