资源
官方文档地址:https://learn.microsoft.com/zh-cn/dotnet/csharp/linq/
简介
查询是一种从数据源检索数据的表达式,查询通常用专门的查询语言来表示。
语言集成查询 (LINQ) 是一系列直接将查询功能集成到 C# 语言的技术统称。
随着时间的推移,人们已经为各种数据源开发了不同的语言;例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。因此,开发人员对于他们必须支持的每种数据源或数据格式,都不得不学习一种新的查询语言。LINQ 通过提供处理各种数据源和数据格式的数据的一致模型,简化了这一情况。在 LINQ 查询中,始终会用到对象。可以使用相同的基本编码模式来查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集、.NET 集合中的数据以及 LINQ 提供程序可用的任何其他格式的数据。
查询操作的三个部分
所有 LINQ 查询操作都由以下三个不同的操作组成:
- 获取数据源。
- 创建查询。
- 执行查询。
数据源
支持 IEnumerable 或派生接口(如泛型 IQueryable)的类型称为可查询类型 。可查询类型不需要进行修改或特殊处理就可以用作 LINQ 数据源。 如果源数据还没有作为可查询类型出现在内存中,则 LINQ 提供程序必须以此方式表示源数据。
查询
LINQ 中,查询的执行不同于查询本身。 换句话说,仅通过创建查询变量不会检索到任何数据。
查询表达式包含三个子句:from、where 和 select。
from 子句指定数据源,where 子句应用筛选器,select 子句指定返回的元素的类型。
虽然看起来和SQL很相似,但注意这些子句的顺序与 SQL 中的顺序是不同的。
查询变量
在 LINQ 中,查询变量是存储查询而不是查询结果的任何变量。更具体地说,查询变量始终是可枚举类型,在 foreach 语句或对其 IEnumerator.MoveNext 方法的直接调用中循环访问时会生成元素序列。
举例:创建一个类型为IEnumerable<TestTableData>
的查询变量testTableDataQuery
,等号后面的是查询表达式。
例中List<TestTableData> list
的结构详见C# 连接MySQL数据库进行增删改查并将结果映射到实体类
List<TestTableData> list = DataTableHelper<TestTableData>.select_data("test_table");
IEnumerable<TestTableData> testTableDataQuery =
from testTableData in list
where testTableData.traycode == "test"
select testTableData;
查询变量的显式和隐式类型化
通常提供查询变量的显式类型以便显示查询变量与 select 子句之间的类型关系。但是,还可以使用 var关键字指示编译器在编译时推断查询变量(或任何其他局部变量)的类型。
因此上述查询也可写为如下所示,即使用var关键字推断查询变量testTableDataQuery
的类型为IEnumerable<TestTableData>
。
List<TestTableData> list = DataTableHelper<TestTableData>.select_data("test_table");
var testTableDataQuery =
from testTableData in list
where testTableData.traycode == "test"
select testTableData;
题外话:事实上LINQ最初的子句顺序是与SQL相同的,即先select,再from,最后where;但这样的话没法推断查询变量的类型,因此改为先from。
基于方法的查询
介绍性的语言集成查询 (LINQ) 文档中的大多数查询是使用 LINQ 声明性查询语法编写的。但是在编译代码时,查询语法必须转换为针对 .NET 公共语言运行时 (CLR) 的方法调用。这些方法调用会调用标准查询运算符(名称为 Where、Select、GroupBy、Join、Max 和 Average 等)。可以使用方法语法(而不查询语法)来直接调用它们。
查询语法和方法语法在语义上是相同的,下面的示例演示一个简单查询表达式以及编写为基于方法的查询的语义上等效的查询。
List<TestTableData> list = DataTableHelper<TestTableData>.select_data("test_table");
//查询表达式
IEnumerable<TestTableData> testTableDataQuery =
from testTableData in list
where testTableData.traycode == "test"
select testTableData;
//基于方法的查询
IEnumerable<TestTableData> testTableDataQuery2 = list.Where(testTableData => testTableData.traycode == "test");//Lambda 表达式
查询表达式
查询表达式是以查询语法表示的查询。
查询表达式必须以 from 子句开头,且必须以 select 或 group 子句结尾。 在第一个 from 子句与最后一个 select 或 group 子句之间,可以包含以下这些可选子句中的一个或多个:where、orderby、join、let,甚至是其他 from 子句。 还可以使用 into 关键字,使 join 或 group 子句的结果可以充当相同查询表达式中的其他查询子句的源。
开始查询表达式
查询表达式必须以 from 子句开头。
查询表达式可能会包含多个 from 子句:在源序列中的每个元素本身是集合或包含集合时,可使用其他 from 子句。
结束查询表达式
查询表达式必须以 group 子句或 select 子句结尾。
可以在 select 或 group 子句中使用 into 关键字创建存储查询的临时标识符。 如果在分组或选择操作之后必须对查询执行其他查询操作,则可以这样做。
select 子句可以用于将源数据转换为新类型的序列,此转换也称为投影。
投影时使用var是必需的,因为查询会生成匿名类型,如下示例。
List<TestTableData> list = DataTableHelper<TestTableData>.select_data("test_table");
//投影
var testTableDataQuery =
from testTableData in list
where testTableData.traycode == "test"
select new
{
column1 = testTableData.traycode,
column2 = testTableData.testtype
};
筛选、排序和联接
开头 from 子句与结尾 select 或 group 子句之间,所有其他子句(where、join、orderby、from、let)都是可选的。 任何可选子句都可以在查询正文中使用零次或多次。
查询执行
延迟执行
查询部分提到了,查询变量本身只存储查询命令,从不保存查询结果。
查询的实际执行将推迟到在 foreach 语句中循环访问查询变量之后进行,迭代变量testTableData
保存了返回的序列中的每个值(一次保存一个值)。
List<TestTableData> list = DataTableHelper<TestTableData>.select_data("test_table");
IEnumerable<TestTableData> testTableDataQuery =
from testTableData in list
where testTableData.traycode == "test"
select testTableData;
//延迟执行
foreach (TestTableData testTableData in testTableDataQuery)
{
Console.WriteLine("{0}, {1}", testTableData.traycode, testTableData.testtype);
}
强制立即执行
要强制立即执行任何查询并缓存其结果,可调用 ToList 或 ToArray 方法。
List<TestTableData> list = DataTableHelper<TestTableData>.select_data("test_table");
//强制立即执行
List<TestTableData> list2 =
(from testTableData in list
where testTableData.traycode == "test"
select testTableData;).ToList();