说明:本内容来自微软的MSDN,好记性不如烂笔头,加深印象。
我的心得:@开头
Linq查询简介
为什么推出Linq?
各种数据源都有自己的查询语言,例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。因此,开发人员不得不针对他们必须支持的每种数据源或数据格式而学习新的查询语言。
从学习查询语言来说,好处?
LINQ 通过提供一种跨各种数据源和数据格式使用数据的一致模型,简化了这一情况——不同的数据源使用不同的查询语言。
查询操作的三个部分
所有 LINQ 查询操作都由以下三个不同的操作组成:
1,获取数据源
2,创建查询
3,执行查询
例子
public class 查询操作的三个部分 { ///<summary> ///演示查询三步走 ///</summary> public voidTestPohe() { // 准备数据源 int[] phone = new int[] { 1, 3, 9, 4, 6, 5, 4, 0, 1 }; // 创建查询 var query = from _p inphone where_p == 1 orderby _p descending select_p; // 执行查询 foreach (var p inquery) { Console.WriteLine(p); } } } |
在 LINQ 中,查询的执行与查询本身截然不同;换句话说,如果只是创建查询变量,则不会检索任何数据。
数据源
在 foreach语句中执行查询,而 foreach要求使用 IEnumerable 或 IEnumerable<(Of <(T>)>)。
支持 IEnumerable<(Of <(T>)>) 或派生接口(如泛型 IQueryable<(Of <(T>)>))的类型称为“可查询类型”。
支持非泛型 IEnumerable 接口的类型(如 ArrayList)也可用作 LINQ 数据源。
查询
from子句指定数据源,where子句应用筛选器,select子句指定返回的元素的类型。
注意的是,在 LINQ 中,查询变量本身不执行任何操作并且不返回任何数据。它只是存储在以后某个时刻执行查询时为生成结果而必需的信息。
查询执行
查询变量本身只是存储查询命令。实际的查询执行会延迟到在 foreach语句中循环访问查询变量时发生。此概念称为“延迟执行”。
由于查询变量本身从不保存查询结果,因此可以根据需要随意执行查询。
应用场景:应用程序持续更新数据库
强制立即执行
1)对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。
2)Count、Max、Average和 First就属于此类查询。
3)由于查询本身必须使用 foreach以便返回结果,因此这些查询在执行时不使用显式 foreach语句。
4)注意,这些类型的查询返回单个值,而不是 IEnumerable集合。
5)若要强制立即执行任意查询并缓存其结果,可以调用 ToList (TSource ) 或 ToArray (TSource ) 方法。
LINQ 和泛型类型
两个基本概念:
1. 当您创建泛型集合类(如 List<(Of <(T>)>))的实例时,您将“T”替换为列表将包含的对象的类型。
2. IEnumerable<(Of <(T>)>)是一个接口,通过该接口,可以使用 foreach语句来枚举泛型集合类。泛型集合类支持 IEnumerable<(Of <(T>)>),就像非泛型集合类(如 ArrayList)支持 IEnumerable。
LINQ 查询中的 IEnumerable<T> 变量
LINQ 查询变量类型化为 IEnumerable<(Of <(T>)>) 或派生类型,如 IQueryable<(Of <(T>)>)。当您看到类型化为IEnumerable<Customer>的查询变量时,这只意味着在执行该查询时,该查询将生成包含零个或多个Customer对象的序列。
IEnumerable<Customer> customerQuery = from cust incustomers where cust.City == "London" selectcust; foreach (Customer customer incustomerQuery) { Console.WriteLine(customer.LastName + ", "+ customer.FirstName); } |
通常,我们建议如果您使用 var,应意识到这可能使您的代码更难以让别人理解。
基本查询操作(LINQ)
获取数据源
在 LINQ 查询中,第一步是指定数据源。像在大多数编程语言中一样,在 C# 中,必须先声明变量,才能使用它。在 LINQ 查询中,最先使用 from子句的目的是引入数据源 (customers) 和范围变量 (cust)。
1) 范围变量类似于 foreach循环中的迭代变量
2) 在查询表达式中,实际上不发生迭代
3) 执行查询时,范围变量将用作对customers中的每个后续元素的引用
4) 其他范围变量可由 let子句引入
筛选
筛选器使查询只返回那些表达式结果为 true 的元素。使用 where子句生成结果。
可以使用熟悉的 C# 逻辑 AND和 OR运算符来根据需要在 where子句中应用任意数量的筛选表达式。
where cust.City=="London" && cust.Name == "Devon"
where cust.City == "London" || cust.City == "Paris"
排序
将返回的数据进行排序。orderby子句将使返回的序列中的元素按照被排序的类型的默认比较器进行排序。
分组
使用 group子句,您可以按指定的键分组结果。
1) 在使用 group子句结束查询时,结果采用列表的列表形式
2) 列表中的每个元素是一个具有Key成员及根据该键分组的元素列表的对象。
3) 在循环访问生成组序列的查询时,您必须使用嵌套的foreach循环。外部循环用于循环访问每个组,内部循环用于循环访问每个组的成员。
// queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>> var queryCustomersByCity = from cust incustomers group cust bycust.City; // customerGroup is an IGrouping<string, Customer> foreach (var customerGroup inqueryCustomersByCity) { Console.WriteLine(customerGroup.Key); foreach (Customer customer incustomerGroup) { Console.WriteLine(" {0}", customer.Name); } } |
4) 引用组操作的结果,可以使用 into关键字来创建可进一步查询的标识符。
// custQuery is an IEnumerable<IGrouping<string, Customer>> var custQuery = from cust incustomers group cust by cust.City into custGroup wherecustGroup.Count() > 2 orderbycustGroup.Key selectcustGroup; |
联接
1)联接运算创建数据源中没有显式建模的序列之间的关联。
2)在 LINQ 中,join子句始终针对对象集合而非直接针对数据库表运行。
3)在 LINQ 中,您不必像在 SQL 中那样频繁使用 join,因为 LINQ 中的外键在对象模型中表示为包含项集合的属性。
4)例如,Customer对象包含Order对象的集合。不必执行联接,只需使用点表示法访问订单:
from order in Customer.Orders...
选择(投影)
1)select子句生成查询结果并指定每个返回的元素的“形状”或类型。
2)当 select子句生成除源元素副本以外的内容时,该操作称为“投影”。
3)使用投影转换数据是 LINQ 查询表达式的一种强大功能。
使用 LINQ 进行数据转换
何谓转换?
将源序列用作输入,并采用多种方式修改它以创建新输出序列
数据本身会改变吗?
通过排序和分组来修改序列本身,而不必修改元素本身。
可以做什么?
· 将多个输入序列合并到具有新类型的单个输出序列中。
· 创建其元素只包含源序列中的各个元素的一个或几个属性的输出序列。
· 创建其元素包含对源数据执行的操作结果的输出序列。
· 创建不同格式的输出序列。例如,您可以将 SQL 行或文本文件的数据转换为 XML。
将多个输入联接到一个输出序列
使用 LINQ 查询来创建包含多个输入序列的元素的输出序列。
选择各个源元素的子集
两种主要方法:
A若要只选择源元素的一个成员,请使用点运算。
var query = from cust in Customers
select cust.City;
B若要创建包含源元素的多个属性的元素,可以使用具有命名对象或匿名类型的对象初始值设定项。
var query = from cust in Customer
select new {Name = cust.Name, City = cust.City};
将内存中的对象转换为XML
在内存中的数据结构、SQL 数据库、ADO.NET 数据集和 XML 流或文档之间转换数据。
对源元素执行操作
输出序列可能不包含源序列的任何元素或元素属性。输出可能是通过将源元素用作输入参数计算出的值的序列。
@类似使用了函数get_name(id)
查询操作中的类型关系(LINQ)
重要吗?
要有效编写查询,应该了解完整的查询操作中的变量类型是如何全部彼此关联的。
A能够更容易地理解文档中的 LINQ 示例和代码示例。
B能了解在使用 var隐式对变量进行类型化时的后台操作。
为什么是强类型?
A LINQ 查询操作在数据源、查询本身及查询执行中是强类型的。
B查询中变量的类型必须与数据源中元素的类型和 foreach语句中迭代变量的类型兼容。
C 此强类型保证在编译时捕获类型错误,以便可以在用户遇到这些错误之前更正它们。
不转换源数据的查询
下图演示不对数据执行转换的 LINQ to Objects 查询操作。源包含一个字符串序列,查询输出也是一个字符串序列。
1. 数据源的类型参数决定范围变量的类型。
2. 选择的对象的类型决定查询变量的类型。此处的name为一个字符串。因此,查询变量是一个 IEnumerable<string>。
3. 在 foreach语句中循环访问查询变量。因为查询变量是一个字符串序列,所以迭代变量也是一个字符串。
转换源数据的查询
下图演示对数据执行简单转换的 LINQ to SQL 查询操作。查询将一个Customer对象序列用作输入,并只选择结果中的Name属性。因为Name是一个字符串,所以查询生成一个字符串序列作为输出。
1. 数据源的类型参数决定范围变量的类型。
2. select语句返回Name属性,而非完整的Customer对象。因为Name是一个字符串,所以custNameQuery的类型参数是 string,而非Customer。
3. 因为custNameQuery是一个字符串序列,所以 foreach循环的迭代变量也必须是 string。
下图演示稍微复杂的转换。select语句返回只捕获原始Customer对象的两个成员的匿名类型。
1. 数据源的类型参数始终为查询中的范围变量的类型。
2. 因为select语句生成匿名类型,所以必须使用var隐式类型化查询变量。
3. 因为查询变量的类型是隐式的,所以 foreach循环中的迭代变量也必须是隐式的。
让编译器推断类型信息
虽然您应该了解查询操作中的类型关系,但是您也可以选择让编译器为您执行全部工作。关键字 var 可用于查询操作中的任何局部变量。下图与前面讨论的第二个示例完全等效。唯一的区别是编译器将为查询操作中的各个变量提供强类型:
查询语法与方法语法(LINQ)
查询语法的由来?
C# 3.0 中引入的声明性查询语法
查询语法本质?
.NET 公共语言运行库 (CLR) 本身并不具有查询语法的概念。因此,在编译时,查询表达式会转换为 CLR 确实了解的内容:方法调用。
标准查询运算符?
这些方法称为“标准查询运算符”,它们具有如下名称:Where、Select、GroupBy、Join、Max、Average等。可以通过使用方法语法而非查询语法直接调用这些方法。
查询语法的应用时机?
建议使用查询语法,因为它通常更简单、更易读
查询语法与方法语法的区别?
A语义上没有区别
方法语法的应用时机?
A匹配指定条件
B检索具有源序列中的最大值的元素的查询
@个人理解:想表达SQL的聚集函数,必须使用方法语法
@方法语法更本质,应多用它。
例子:
int[] numbers = { 5, 10, 8, 3, 6, 12}; //Query syntax: IEnumerable<int> numQuery1 = from num innumbers wherenum % 2 == 0 orderbynum selectnum; //Method syntax: IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n); |