C# LINQ查询详解(附带实例)
LINQ 全名为 Language Integrated Query,是 Microsoft 公司开发的语言集成查询,目前这项技术已经整合至 C# 语言。
过去我们学习数据库,如 SQL、XML文件、各种 Web 服务等时,必须针对每一种数据库学习查询语言,现在 Microsoft 公司将查询功能整合,设计了 LINQ,在这个架构下有相同的类、方法和事件。
因此可以使用 LINQ 技术同时应用在下列领域:
对于撰写数据库查询语言的程序设计师而言,LINQ 最明显的特色是使用声明式查询语法,通过使用查询语法,可以用最少的程序代码对数据来源进行排序、筛选或分组。当然其最大的特色是,相同的查询运算语法可以同时应用在 SQL 数据库、ADO.NET 数据集、XML 文件以及 .NET 集合中。
LINQ 已经是不同程序语言的共享技术,可以作为不同程序语言和不同数据库之间的桥梁,如下图所示。
【实例】 from 和 select 的基础应用,这个程序筛选出所有 arr 数组的内容,读者可以注意第 3 行,其使用 var 来声明查询结果变量 query。
where 可以搭配关系表达式来筛选数据。例如,扩充实例 1,增加 where,筛选小于 10 的数组内容。
假设现在要求读者筛选大于 5 且小于 15 的数据,这时需要使用 C# 的逻辑运算符“&&”,这将是读者的习题。where 可以搭配 bool 的条件式来筛选数据。
【实例】筛选偶数数据。
我们也可以将类数据用于筛选数据。例如,筛选主修是 CS(Computer Science) 的学生:
【实例】将句子拆解为单词,然后输出以 a、i、t 开头的单词。
【实例】 orderby 关键词的应用,第 3~8 行是执行从小到大排序,第 11~14 行是执行从大到小排序。
【实例】将 10 以下数字分奇数和偶数。
【实例】 join 方法的应用,这个程序会查询相同元素的内容。
【实例】 join 方法的应用,这个程序会列出 a 小于 5,b 大于 1,同时 a 等于 b 的结果。
【实例】 join 方法的应用,这个程序会进行姓名 (Name) 和学院 (College) 的配对。
过去我们学习数据库,如 SQL、XML文件、各种 Web 服务等时,必须针对每一种数据库学习查询语言,现在 Microsoft 公司将查询功能整合,设计了 LINQ,在这个架构下有相同的类、方法和事件。
因此可以使用 LINQ 技术同时应用在下列领域:
- LINQ to Objects:又称 LINQ to Collection,只要是属于 IEnumerable 或是 IEnumerable<T> 的集合都可以使用 LINQ 技术。
- LINQ to SQL:可以处理 SQL 数据库的数据。
- LINQ to XML:可以处理 XML 文件、XML 片段、XML 格式的字符串。
- LINQ to DataSet:可以处理 DataSet 中的数据。
对于撰写数据库查询语言的程序设计师而言,LINQ 最明显的特色是使用声明式查询语法,通过使用查询语法,可以用最少的程序代码对数据来源进行排序、筛选或分组。当然其最大的特色是,相同的查询运算语法可以同时应用在 SQL 数据库、ADO.NET 数据集、XML 文件以及 .NET 集合中。
LINQ 已经是不同程序语言的共享技术,可以作为不同程序语言和不同数据库之间的桥梁,如下图所示。

C# LINQ语法
LINQ 有 8 个基本表达式,可以参考下表:表达式 | 说明 |
---|---|
from | 指定查询操作的数据来源,和变量的范围 |
select | 筛选查询结果的类型和形式 |
where | 筛选的逻辑条件 |
let | 在数据查询过程中,我们可以使用 let 关键词创建和存储子表达式的结果,未来可以使用子表达式的结果作为查询的依据 |
orderby | 针对查询结果排序操作 |
group ... by | 对查询结果分组,每个分组是一个数组,然后可以用 foreach 输出分组元素 |
into | 创建临时标识符,用于存储 group、join、select 子句的执行结果 |
join | 依据键值连接多个序列,返回查询的结果 |
1) from/select/where实例
使用 LINQ 习惯上会使用 var 来声明隐型变量,这可以简化设计,然后变量真正的数据类型由语句右边的类型决定,也就是在程序编译时决定数据类型。右边推断的类型可能是 C# 内建类型、匿名类型、使用者自定义的类型,或是 .NET 类库中定义的类型。【实例】 from 和 select 的基础应用,这个程序筛选出所有 arr 数组的内容,读者可以注意第 3 行,其使用 var 来声明查询结果变量 query。
int[] arr = new int[] { 1, 3, 5, 11, 21, 12, 6, 9, 20 }; var query = from n in arr select n; foreach (var q in query) Console.Write($"{q}, ");执行结果为:
1, 3, 5, 11, 21, 12, 6, 9, 20,
where 可以搭配关系表达式来筛选数据。例如,扩充实例 1,增加 where,筛选小于 10 的数组内容。
int[] arr = new int[] { 1, 3, 5, 11, 21, 12, 6, 9, 20 }; var query = from n in arr where n < 10 // 筛选 n < 10 select n; foreach (var q in query) Console.Write($"{q},");执行结果为:
1,3,5,6,9,
假设现在要求读者筛选大于 5 且小于 15 的数据,这时需要使用 C# 的逻辑运算符“&&”,这将是读者的习题。where 可以搭配 bool 的条件式来筛选数据。
【实例】筛选偶数数据。
int[] arr = new int[] { 1, 3, 5, 11, 21, 12, 6, 9, 20 }; var query = from n in arr where (n % 2 == 0) // 筛选偶数 select n; foreach (var q in query) Console.Write($"{q},");执行结果为:
12,6,20,
我们也可以将类数据用于筛选数据。例如,筛选主修是 CS(Computer Science) 的学生:
var students = new List<Student>() { new Student(){Id = 1, Name = "Mary", Major = "cs"}, new Student(){Id = 2, Name = "Tom", Major = "EE"}, new Student(){Id = 3, Name = "Sam", Major = "cs"}, new Student(){Id = 4, Name = "John", Major = "cs"} }; // 查询所有主修是CS的学生 var query = from s in students where s.Major == "cs" select s; // 输出结果 foreach (var q in query) Console.WriteLine($"{q.Id}, {q.Name}"); public class Student { public int Id { get; set; } public string Name { get; set; } public string Major { get; set; } }执行结果为:
1, Mary
3, Sam
4, John
2) let实例
【实例】let 关键词的应用,这个程序会输出开头字母是 c~k 的英文单词。var keywords = new[] { "bool", "break", "byte", "catch", "char", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "float", "for", "foreach", "ref", "return", "short", "sizeof", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "using", "virtual", "void", "volatile", "while" }; var query = from words in keywords let firstLetter = words[0] // 取第1个字母 where firstLetter >= 'c' && firstLetter <= 'd' select words; foreach (var q in query) Console.Write($"{q}, ");执行结果为:
catch, char, delegate, do, double,
【实例】将句子拆解为单词,然后输出以 a、i、t 开头的单词。
string[] news = { "Apple introduced Safety Check.", "This robust security setting.", "You may stop sharing your information." }; // 将句子拆成单字数组,选择 'a'、'i'、't' 开头的单词 var query = from sentence in news let words = sentence.Split(' ') // 拆解句子成单词数组 from word in words let w = word.ToLower() // 单词转成全部小写 where w[0] == 'a' || w[0] == 'i' || w[0] == 't' select word; // 输出结果 foreach (var v in query) Console.WriteLine($"{v} 是a、i、t开头单词");执行结果为:
Apple 是a、i、t开头单词
introduced 是a、i、t开头单词
This 是a、i、t开头单词
information. 是a、i、t开头单词
3) orderby实例
使用 orderby 可以执行排序操作,默认是从小到大排序,使用关键词可以执行从大到小排序。【实例】 orderby 关键词的应用,第 3~8 行是执行从小到大排序,第 11~14 行是执行从大到小排序。
int[] arr = new int[] { 8, 3, 5, 11, 21, 12, 6, 9, 20 }; var query1 = from n in arr where n < 10 // 筛选 n < 10 orderby n // 默认从小到大排序 select n; foreach (var q in query1) Console.Write($"{q}, "); Console.WriteLine(); // 跳行输出 var query2 = from n in arr where n < 10 // 筛选 n < 10 orderby n descending // 从大到小排序 select n; foreach (var q in query2) Console.Write($"{q}, ");执行结果为:
3, 5, 6, 8, 9,
9, 8, 6, 5, 3,
4) group by实例
分组是 LINQ 强大的功能之一,可以执行下列分组策略:- 根据单一属性;
- 根据属性的第一个字母;
- 根据数字区间;
- 根据布尔值或其他表达式;
- 根据复合索引键。
【实例】将 10 以下数字分奇数和偶数。
int[] arr = new int[] { 8, 3, 5, 11, 21, 12, 6, 9, 20 }; var query = from n in arr where n < 10 // 筛选 n < 10 group n by n % 2; // 分组奇数和偶数 foreach (var element in query) // 元素 element 是分组数组 { foreach (var e in element) // 列出分组数组内容 Console.WriteLine(e); Console.WriteLine("=====group组别分隔线====="); }执行结果为:
8
6
=====group组别分隔线=====
3
5
9
=====group组别分隔线=====
5) group by/into实例
当执行分组后,分组的键值可以用 Key 属性取得。例如,将 Student 类的数据依据科系主修分组。var students = new List<Student>() { new Student(){Id = 1, Name = "Mary", Major = "cs"}, new Student(){Id = 2, Name = "Tom", Major = "EE"}, new Student(){Id = 3, Name = "Sam", Major = "cs"}, new Student(){Id = 4, Name = "John", Major = "cs"}, new Student(){Id = 5, Name = "Kevin", Major = "EE"}, new Student(){Id = 6, Name = "Linda", Major = "cs"} }; // 依科系分组 var query = from student in students group student by student.Major into newGroup select newGroup; // 输出结果 foreach (var qGroup in query) { Console.WriteLine($"科系:{qGroup.Key}"); // Key是分组属性 foreach (var student in qGroup) // 遍历分组 Console.WriteLine($"\t{student.Id}, {student.Name}"); } public class Student { public int Id { get; set; } public string Name { get; set; } public string Major { get; set; } }执行结果为:
科系:cs
1, Mary
3, Sam
4, John
6, Linda
科系:EE
2, Tom
5, Kevin
6) join实例
关键词 join 用于连接数据,基本上需要外部序列 (outer sequence)、内部序列 (inner sequence)、键值 (key selector) 和结果选择,整个语法如下:from … in 外部序列 join … in 内部序列 on 外部键值 equals 内部键值 select
【实例】 join 方法的应用,这个程序会查询相同元素的内容。
int[] outer = new int[] { 1, 2, 3, 4, 5, 6, 7 }; int[] inner = new int[] { 1, 3, 4, 7 }; var query = from a in outer // 取得outer join b in inner // 连接inner on a equals b // 筛选元素 a = b select b; // 筛选结果 foreach (var item in query) Console.WriteLine(item);执行结果为:
1
3
4
7
【实例】 join 方法的应用,这个程序会列出 a 小于 5,b 大于 1,同时 a 等于 b 的结果。
int[] outer = new int[] { 1, 2, 3, 4, 5, 6, 7 }; int[] inner = new int[] { 1, 3, 4, 7 }; var query = from a in outer // 取得outer where a < 5 // 设定 a < 5 join b in inner // 连接inner on a equals b // 筛选元素 a = b where b > 1 // 设定 b > 1 select b; // 筛选结果 foreach (var item in query) Console.WriteLine(item);执行结果为:
3
4
【实例】 join 方法的应用,这个程序会进行姓名 (Name) 和学院 (College) 的配对。
IList<Student> studentList = new List<Student>() { new Student(){ID = 1, Name = "Tom", Major = "CS"}, new Student(){ID = 2, Name = "Kevin", Major = "ME"}, new Student(){ID = 3, Name = "Bill", Major = "History"}, new Student(){ID = 4, Name = "Jonny", Major = "Language"}, new Student(){ID = 5, Name = "Mike", Major = "cs"} }; IList<School> schoolList = new List<School>() { new School(){Major = "cs", College = "abc"}, new School(){Major = "ME", College = "abc"}, new School(){Major = "MBA", College = "Business"} }; var query = from s in studentList // outer join st in schoolList // inner on s.Major equals st.Major // 筛选科系相同 select new { name = s.Name, // 姓名 major = s.Major, // 主修 college = st.College // 学院 }; foreach (var q in query) Console.WriteLine($"{q.name} 就读 {q.college} 学院主修是 {q.major}"); public class Student { public int ID { get; set; } public string Name { get; set; } public string Major { get; set; } } public class School { public string Major { get; set; } public string College { get; set; } }执行结果为:
Tom 就读 abc 学院主修是 CS
Kevin 就读 abc 学院主修是 ME
Mike 就读 abc 学院主修是 cs