Linq 杂记(读书笔记)

时间:2021-11-07 16:10:58

LINQ 查询基础 杂记

基本概念

  1. LINQ : Language Integrate Query ,.Net3.5新增内容
  2. 简化数据交互操作
    对象与数据之间建立一种对应关系,使用访问内存对象的方式查询数据。
  3. 增强类型安全性
    查询表达式访问的数据源是IEnumerable<T>IQueryable<T>类型的数据集合对象,返回的结果亦是。
  4. LINQ 查询表达式访问的是对象,该对象可以表示各种类型的数据源。
    例如:SQL Server数据库,XML文档,ADO.Net数据集,内存中的对象
  5. 相关类库在System.Linq命名空间下。
  6. 核心接口
    IEnumerable<T> : 表示数据源和查询结果的数据集合
    IQueryable<T> : 继承IEnumerable接口,表示可以查询的表达式目录树
    核心类
    Enumerable : 对 IEnumerbale提供扩展方法,实现 LINQ 标准查询运
    算符, 例:过滤、导航、排序、查询、联接、求和、求最大值、求最小值
    Queryable:对 IQueryable提供扩展方法,实现 LINQ 标准查询运算符。
  7. IEnumerable与IQueryable区别
  8. LINQ 四个主要技术方向
    LINQ to Object , LINQ to ADO.Net , LINQ to XML , .Net为用户扩展LINQ提供了支持

LINQ基本使用

引用命名空间 System.Linq,
使用LINQ to XML 要引用 System.Xml.Linq 命名空间,
使用 LINQ to ADO.NET 要引用System.Data.Linq 命名空间


static void Main(string[] args)
{
int[] ary = {1, 2, 5, 4, 3, 9, 8, 7 }; //定义数组
var query1 = from val in ary
select val; //定义查询语句
foreach (var item in query1)
{
System.Console.Write("{0} ", item); //打印输出
}
}

LINQ 查询的基本元素

数据源 : 通常是一个或多个数据集,数据集是一个类型为 IEnumerable 或IQueryable的对象
目标数据 : 表示查询所需要的数据,对应 SQL语句中定义需要查询表中的字段
筛选条件 : 对应SQL语句中where子句,对数据源元素的过滤条件
附加操作 : 对查询结果的操作,排序|求和|分组

数据源目标数据是必备元素

LINQ 查询表达式

关键字

关键字 说明
from 指定范围变量和数据源
where 根据bool表达式从数据源中筛选数据
select 指定查询结果中的元素所具有的类型或表现形式
group 对查询结果按照键值进行分组(IGrouping)
into 提供一个标识符,它可以充当对join、group或select子句结果的引用
orderby 对查询出的元素进行排序(ascending/descending)
join 按照两个指定匹配条件来Equals连接两个数据源
let 产生一个用于存储查询表达式中的子表达式查询结果的范围变量

from 指定数据源

from localVal in dataSource

from子句 无需为localVal元素指定数据类型,编译器会根据数据源类型分配合适类型。
特殊情况下,需要指定数据类型的情况下,可使用如以下实例方式

from object localVal in dataSource

注意使用上述方式编写,编译器不会为localVal检查具体类型,当类型错误是编译时不会报错

select 指定目标数据

select element

select 子句中如果不指定元素的具体类型,编译器会将查询中元素的类型自动设置为select 子句中元素的具体类型,如下实例,没有指定查询类型,自动判断为int类型

from val1 in new int[] { 1,33,4,21 }
select val1

select 子句中要选择的目标数据不仅可以为数据源中的元素,还可以是该元素的属性、方法、运算等

from localVal in dataSource
select localVal.column1

from localVal in dataSource
select localVal.column1.length

有时候,我们只需要获取元素部分属性或方法的数据,可以使用匿名类型

from localVal in dataSource
select new {
attr1 = localVal.column1 ,
attr2 = localVal.column2
}

也可以将其定义为强类型

public class resultClass{
string attr1;
string attr2;
}

from localVal in dataSource
select new resultClass() {
attr1 = localVal.column1 ,
attr2 = localVal.column2
}

where 定义筛选条件

where expression

两个并列的where子句进行多个条件过滤,查询结果必须满足所有where子句条件。
尽量保持where子句条件简短易懂,当出现很多逻辑并的条件时,可以用多个where子句代替 &&

orderby 排序

orderby element [sortType]

sortType 表示排序类型,升序(ascending) 降序(desending),可忽略默认值为升序。

注:当多个orderby子句时,以最后一个orderby子句排序
多个查询条件 按先后顺序逗号分隔

group 分组

group element by key

element表示查询结果返回的元素,key为分组条件。
group 返回类型为 IGrouping

from子句 复合查询

复合查询分为两种:

  1. 对同一个数据源中的元素进行嵌套查询,数据源的元素通常包含一个可以作为数据源的属性、方法等。外层 from子句对数据源进行查询,内层 from 子句则对元素中的数据源进行查询。如示例 1
  2. 在多个数据源上进行查询。 如示例2

示例 1:

var query1 = from st in stAry
from scr in st.Scores
where scr.Score > 80
group new { st.Name, scr } by st.Name;

示例 2:

int[] intAry1 = { 5, 15, 25, 30, 33, 50 };
//创建两个整数数组 intAry1 和 intAry2 作为数据源
int[] intAry2 = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
//查询 query1 从两个数据源 intAry1 和 intAry2中获取数据
var query2 = from val1 in intAry1
from val2 in intAry2
where val2 % val1 == 0
//group val2 by val1;
select new { val1 = val1, val2 = val2 };
foreach (var item in query2) //打印查询 query1 的元素
{
System.Console.Write("{0}: ", item.val1);
System.Console.Write("{0} ", item.val2);
System.Console.WriteLine();
}

join 子句进行联接

内部联接、分组联接、左外部联接


内部联接

join element in dataSource on exp1 equals exp2

var query1 =from val1 in intAry1
join val2 in intAry2 on val1 % 5 equals val2 % 15
select new {VAL1=val1, VAL2=val2};
foreach (var item in query1) //打印查询 query1 的元素
{
System.Console.WriteLine(item);
}

输出结果为:

{ VAL1 = 5, VAL2 = 30 }
{ VAL1 = 5, VAL2 = 60 }
{ VAL1 = 15, VAL2 = 30 }
{ VAL1 = 15, VAL2 = 60 }
{ VAL1 = 25, VAL2 = 30 }
{ VAL1 = 25, VAL2 = 60 }
{ VAL1 = 30, VAL2 = 30 }
{ VAL1 = 30, VAL2 = 60 }
{ VAL1 = 40, VAL2 = 30 }
{ VAL1 = 40, VAL2 = 60 }

分组联接

查询结果按照第一个数据集中的元素进行分组

int[] intAry1 = { 5, 15, 25, 30, 33, 40 };
//创建整数数组 intAry1 作为数据源
int[] intAry2 = { 10, 20, 30, 50, 60, 70, 80 };
//创建整数数组 intAry2 作为数据源
//查询 query1 使用 join 子句从两个数据源获取数据
//演示分组联接的使用
var query =
from val1 in intAry1
join val2 in intAry2 on val1 % 5 equals val2 % 15 into val2Grp
select new { VAL1 = val1, VAL2GRP = val2Grp };
foreach (var obj in query) //打印查询 query 的元素
{
System.Console.Write("{0}: ", obj.VAL1);
foreach (var val in obj.VAL2GRP) //打印分组内部的元素
{
System.Console.Write("{0} ", val);
}
System.Console.WriteLine();
}

输出结果为:

5: 30 60
15: 30 60
25: 30 60
30: 30 60
33:
40: 30 60

左外部联接

返回第一个集合中的所有元素,无论它是否在第二个集合中有相关匹配的元素

需要用到的DefaultIfEmpty()方法,如下示例

int[] intAry1 = { 5, 15, 23, 30, 33, 40 };
//创建整数数组 intAry1 作为数据源
int[] intAry2 = { 10, 20, 30, 50, 60, 70, 80 };
//创建整数数组 intAry2 作为数据源
//查询 query1 使用 join 子句从两个数据源获取数据
//演示左联接的使用
var query1 = from val1 in intAry1
//将匹配的val2中匹配的元素存入val2Grp
join val2 in intAry2 on val1 % 5 equals val2 % 15 into val2Grp
//对val2Grp进行处理,若没有匹配的元素设为默认值,赋值到grp
from grp in val2Grp.DefaultIfEmpty()
select new { VAL1 = val1, VAL2GRP = grp };
foreach (var obj in query1) //打印查询 query1 的元素
{
System.Console.WriteLine("{0}", obj);
}

左外部联接是在分组联接的查询结果上再进行一次查询,所以它在 join 之后还有一个 from 子句进行查询。

LINQ 查询方法

IEnumerable<T> 接口 (继承IEnumerable接口)

  1. 支持在指定数据集合上进行迭代操作,定义了一组对数据集合 遍历、过滤、排序、搜索、定位等操作的扩展方法。例如: 数值运算(Sum、Min、Max、Average)、元素数量(Count、LongCount)、取值(First、Last、ElementAt 等)、提取子集(Skip、SkipWhile、Take、TakeWhile)、集合操作(Reverse、Concat、Distinct、Except、
    Intersect、Union、SequenceEqual 等)
    (查看更多)
  2. 在 LINQ 中,数据源实际上是实现了接口 IEnumerable的类,通过 select 子句返回的查询结果也是一个实现了接口 IEnumerable的类。

IQueryable<T> 接口 (由IEnumerable<T> 接口派生)

使用上和IEnumerable<T>类似
IQueryable<T>与IEnumerable<T> 接口的区别

Lambda 表达式

  1. 实际上是一个匿名函数
  2. 表达式的左边是输入参数(可能没有),右边是表达式或语句块

示例

(input parameters) => expression

LINQ 查询操作都是在IEnumerable<T>类型的对象上操作,LINQ表达式都需要转成具体的函数调用执行。
LINQ 表达式的关键字与IEnumerable<T>接口方法之间存在对应关系
IEnumerable的方法 查询表达式关键字 功 能
Cast() from 子句指定元素类型 使用显式类型化的范围变量,例如:
from int val in intAry

IEnumerable的方法 查询表达式关键字 功能
Cast() from 子句指定元素类型 使用显式类型化的范围变量,例如:from int val in intAry
group…by…into 对查询结果进行分组
GroupJoin() join…in…on…equals…into 左外联接查询
Join() join…in…on…equals 内部联接查询
OrderBy() orderby 从小到大排序
OrderByDescending() Orderby…descending 从大到小排序
Select() select 指定影射元素
SelectMany() select 从多个 from 子句进行查询
ThenBy() Orderby…,… 多个排序元素,后一个排序元素按从小到大排序
ThenByDescending() Orderby…,…descending 多个排序元素,后一个排序元素按从大到小排序
Where() Where 条件过滤

上述 IEnumerable<T> 对应方法的传参,就涉及到Lambda表达式。(也可使用委托函数)

int[] intAry = {2, 4, 5, 8, 9, 11}; //定义数组
var query1 = intAry.Where(num => num%2 == 0); //设置查询条件
foreach (var val in query1) //打印输出
{
System.Console.Write("{0} ", val);
}

OrderBy OrderByDescending 排序方法

该方法需要特别说明下,如果只是普通的排序,那很简单。如下示例即可

int[] intAry = { 3, -2, 5, 8, 3, 4, 2, 19, 20 };
var query1 = intAry.OrderBy(val => val);

上述示例使用的默认比较器,若默认比较器无法满足需求或者比较逻辑较复杂,则需自定义比较器并继IComparer接口

class MyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return x.length - y.length;
}
}

Skip()、SkipWhile()跳过元素

该方法通常用于分页处理。

int[] intAry = { 3, -2, 5, 8, -13, -4, 12, -19, 20 };
var query1 = intAry.Skip(3);//按数量跳过
//跳过从下标零的位置开始,连续不能被10整除的元素直到第一个可以被整除的元素到后面的所有元素
var query2 = intAry.SkipWhile(num => num % 10 > 0);

Take()、TakeWhile() 提取元素

该方法通常用于分页处理。

int[] intAry = { 3, -2, 5, 8, -13, -4, 12, -19, 20 };
var query1 = intAry.Take(3);//按数量提取前几个
//提取从下标零的位置开始,连续不能被10整除的元素直到第一个可以被整除的元素到后面的所有元素
var query2 = intAry.TakeWhile(num => num % 10 > 0);

其他常用方法

Min()、Max() 获取最大值、最小值
Distinct() 去除重复
Concat() 连接两个集合
Union() 合并查询结果

(查看更多)

小结

什么是 LINQ?为什么要使用 LINQ?
Language Integrate Query , 一种ORM数据库交互方式
操作方便,访问数据库像访问内存中的数据一样便捷, 且增加类型安全性

如何在 C#中使用 LINQ。
使用 IEnumerable 接口的方法,或使用LINQ表达式

什么是 LINQ 查询表达式?
from 子句有什么用,多重 from 子句如何使用。
select 子句如何使用。
where 子句如何使用,如何实现多个 where 子句。
orderby 子句如何使用,如何实现多个关键字排序。
group 子句分组如何实现。
join 子句如何使用。
IEnumerable接口及其成员有什么作用。
Lambda 表达式如何使用。
IEnumerable接口成员和 LINQ 查询表达式的关系。
如何在集合中进行数据提取。
如何在集合中进行数值运算。
如何在对多个集合进行集合操作。