第7章:初识Linq
第7章:Linq
本章目标
- 掌握Linq的运用
本章内容
Linq概述
什么是Linq
Lanaguage Interated Query(语言集成查询),Linq 是集成C# 和VB这些语言中用于提供数据查询能力的一个新特性。Linq用于以对象形式管理关系数据,并提供了丰富的查询功能。
LINQ是一组语言特性和API,使得你可以使用统一的方式编写各种查询。用于保存和检索来自不同数据源的数据,从而消除了编程语言和数据库之间的不匹配,以及为不同类型的数据源提供单个查询接口。
LINQ总是使用对象,因此你可以使用相同的查询语法来查询和转换XML、对象集合、SQL数据库、ADO.NET数据集以及任何其他可用的LINQ提供程序格式的数据。
LInq支持的数据源
- LINQ to Objects 主要负责对象的查询。
- LINQ to XML 主要负责XML的查询。
- LINQ to ADO.NET 主要负责数据库的查询。
- LINQ to SQL
- LINQ to DataSet
- LINQ to Entities
Linq的优势
1、熟悉的语言:开发人员不必为每种类型的数据源或数据格式学习新的语言。
2、更少的编码:相比较传统的方式,LINQ减少了要编写的代码量。
3、可读性强:LINQ增加了代码的可读性,因此其他开发人员可以很轻松地理解和维护。
4、标准化的查询方式:可以使用相同的LINQ语法查询多个数据源。
5、类型检查:程序会在编译的时候提供类型检查。
6、智能感知提示:LINQ为通用集合提供智能感知提示。
7、整形数据:LINQ可以检索不同形状的数据。
匿名类型
匿名类型是可以创建无名类型的一种类型,经常用于LINQ查询的结果中。
匿名类型只能和局部变量配合使用,不能用于类成员。
匿名类型没有名字,必须使用var关键字作为变量类型。
编译器为匿名对象创建的属性是只读的,所以不能设置匿名类型对象的属性。
格式:
1
2
3
4
5
6static void Fun1()
{
var man = new { Name="孙悟空", Age = 25, Sex='男' };
Console.WriteLine("姓名:{0}, 年龄:{1}, 性别:{2}", man.Name, man.Age, man.Sex);
}
方法语法和查询语法
初识查询语法和方法语法
我们使用LINQ查询时可以使用两种形式的语法:方法语法和查询语法。
方法语法:使用标准的方法调用,这些方法是一组叫做标准查询运算符的方法。方法语法命令式的,指明了查询方法调用的顺序。
查询语法:类似于SQL语句,使用查询表达式形式书写。查询语法是声明式的,查询描述的是你想返回的东西,但并没有指明如何执行这个查询。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27static void Fun2()
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//查询语法
IEnumerable<int> result1 = from n in nums
where n > 4 && n < 8
select n;
//方法语法
IEnumerable<int> result2 = nums.Where(x => (x > 4 && x < 8));//返回枚举
//两种形式的组合
int count = (from n in nums where n > 4 && n < 8 select n).Count();//返回单个值
//查看结果
foreach (int i in result1)
Console.Write(i);
Console.WriteLine();
foreach (int i in result2)
Console.Write(i);
Console.WriteLine();
Console.Write(count);
}
查询变量
LINQ查询可以返回两种类型的结果,如下:
枚举:包含了一组数据
标量:一个单一的值
理解查询变量:
- 如果查询表达式返回枚举,查询一直到处理枚举时才会执行。(延迟执行)
- 如果枚举被处理多次,查询就会执行多次。
- 如果在进行遍历之后,查询执行之前数据有改动,则查询会使用新的数据。
- 如果查询表达式返回标量,查询立即执行,并把结果保存在查询变量中。
查询表达式的结构
查询表达式由查询体后的from子句组成。
- 子句必须按照一定的顺序。
- from子句和select…group这两部分是必需的。
- 其他子句是可选的。
- 可以有任意多的from…let…where子句。
from子句
from子句和foreach子句格式十分相似,但实际有很大区别:
- foreach语句命令式指定了按顺序一个个访问集合中的项。from子句只是声明式地规定集合中的每个项都要访问,并没有指定顺序。
- foreach在遇到代码时就执行其主体。from子句什么也不执行,只有在遇到访问查询变量的语句时才会执行。
格式:
代码示例:
1
2
3
4
5
6
7
8
9
10
11static void Fun3()
{
int[] ints = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var nums = from n in ints
where n > 7 //使用迭代变量n
select n; //使用迭代变量n
foreach (var n in nums)
Console.WriteLine(n);
}
join子句
注意事项:
- 使用联结来结合两个或更多集合中的数据。
- 联结对象接受两个集合,然后创建一个临时的对象集合,每一个对象包含原始集合对象中的所有字段。
- 字段只能使用equals比较,不能使用==
格式:
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46/// <summary>
/// 学生
/// </summary>
class Student
{
/// <summary>
/// 学号
/// </summary>
public string StudentNo { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 性别
/// </summary>
public char Sex { get; set; }
}
/// <summary>
/// 成绩
/// </summary>
class Result
{
/// <summary>
/// 学号
/// </summary>
public string StudentNo { get; set; }
/// <summary>
/// 科目
/// </summary>
public string Subject { get; set; }
/// <summary>
/// 分数
/// </summary>
public int Score { get; set; }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28static void Fun4()
{
//学生
Student[] students = new Student[] {
new Student { StudentNo="GCKJ001", Name="曾令耀", Age=18,Sex='男' },
new Student { StudentNo="GCKJ002", Name="李尚驰", Age=18,Sex='男' },
new Student { StudentNo="GCKJ003", Name="黄朗云", Age=18,Sex='女' }
};
//成绩
Result[] courseStudents = new Result[] {
new Result { StudentNo="GCKJ001",Subject="java"},
new Result { StudentNo="GCKJ002",Subject="c#"},
new Result { StudentNo="GCKJ002",Subject="python"},
new Result { StudentNo="GCKJ003",Subject="python"},
new Result { StudentNo="GCKJ001",Subject="c#"},
new Result { StudentNo="GCKJ003",Subject="java"},
};
//查询所有选择了c#课的学生
var query = from s in students
join c in courseStudents on s.StudentNo equals c.StudentNo
where c.Subject == "c#"
select s.Name;
foreach (var e in query)
Console.WriteLine(e);
}
from…let…where片段
可选的from…let…where部分是查询主体的第一部分,可以由任意数量的from子句、let子句、where子句来组合。
多个from子句
1
2
3
4
5
6
7
8
9
10
11
12
13static void Fun5()
{
var groupA = new[] { 3, 4, 5, 6 };
var groupB = new[] { 6, 7, 8, 9 };
var someInts = from a in groupA //必需的第一个from子句
from b in groupB //查询主体的第一个from子句
where a > 4 && b <= 8
select new { a, b, sum = a + b };//匿名类型对象
foreach (var e in someInts)
Console.WriteLine(e);
}let子句
let子句接受一个表达式的运算并且把它赋值给一个需要在其他运算中使用的标识符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14static void Fun6()
{
var groupA = new[] { 3, 4, 5, 6 };
var groupB = new[] { 6, 7, 8, 9 };
var someInts = from a in groupA
from b in groupB
let sum = a + b
where sum == 12
select new { a, b, sum };
foreach (var e in someInts)
Console.WriteLine(e);
}多个where子句
where子句根据之后的运算来去除不符合条件的项。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15static void Fun7()
{
var groupA = new[] { 3, 4, 5, 6 };
var groupB = new[] { 6, 7, 8, 9 };
var someInts = from a in groupA
from b in groupB
let sum = a + b
where sum >= 11 //选择sum>=11的
where a == 4 //选择a==4的
select new { a, b, sum };//存放满足两种条件的匿名类
foreach (var e in someInts)
Console.WriteLine(e);
}
orderby子句
orderby接受一个表达式,并根据表达式按顺序返回结果项。orderby子句默认升序,可以使用ascending和descending来指定升序还是降序 .
1 | static void Fun8() |
select子句
select子句指定所选对象的哪部分应该被选择。
1 | static void Fun9() |
查询中的匿名类型
可以在select子句后创建匿名类型存放指定查询的数据。
1 | static void Fun10() |
group子句
group子句把select的对象根据一些标准进行分组。
1 | static void Fun11() |
查询延续:into子句
查询延续子句可以接受查询的一部分结果并赋予一个名字,从而可以在查询的另一部分中使用。
1 | static void Fun12() |
Linq应用综合案例
1 | static void Fun13() |
Linq to xml
前序
操作之前,需要引入 程序集和命名空间 System.Xml.Linq;
核心对象:
对象 | 描述 |
---|---|
XDocument | xml文档 |
XElement | xml元素/结点 |
XDeclaration | xml声明 |
XComment | xml注释 |
XAttribute | xml属性 |
生成XML
1.创建简单的xml
1 | /// <summary> |
2.创建带注释的xml
1 | /// <summary> |
3.根据对象创建xml
1 | /// <summary> |
4.创建带属性的xml
1 | /// <summary> |
修改XML
新建一个test1.xml文件,并在其中写入如下内容:
1 |
|
1.通过文件读取xml
1 | /// <summary> |
2.在指定节点前后添加新节点
1 | /// <summary> |
3.添加属性到节点中
1 | /// <summary> |
4.添加注释到指定节点前后
1 | /// <summary> |
5.替换指定节点
1 | /// <summary> |
6.删除指定属性
1 | /// <summary> |
7.删除指定节点
1 | /// <summary> |
查询XML
下新建一个test2.xml并写入如下内容:
1 |
|
1.显示指定节点的所有父节点
1 | /// <summary> |
2.显示指定节点的所有子节点
1 | /// <summary> |
3.显示同级节点之前的节点
1 | /// <summary> |
4.显示同级节点后面的节点
1 | /// <summary> |
本章总结
课后作业
1>已有整型数组nums包含10个数,完成以下功能:
1.查询大于50的偶数的数量
2.查询所有偶数的平均值
3.查询所有奇数中的最小数
4.查询所有偶数中的最大数
5.查询大于50的所有数字的平均值。
6.查询所有奇数,且降序排序
2>已有学生数组stus,包含10个学生对象(Name,Age,Sex),完成以下功能:
1.查询所有男生的数量
2.查询男生中年龄最大的学生姓名
3.查询女生中年龄最小的学生姓名
4.查询所有已成年的学生的年龄的平均值
5.查询姓“张”的学生人数
6.查询姓“张”的学生中,年龄最大的学生
7.查询姓“张”的学生,且按年龄升序