由于个人在学校没有接触过Linq,而且在工作上运用Linq的时候也比较多,准备把LINQ的相关知识学习整理梳理一遍,希望能填补下这个知识点,也为未来减轻压力。
LINQ查询表达式使用C#常见的语言构造,从外观上看,和我们常用的SQL类似,并且查询表达式中的变量可以用匿名类型,所以在很多情况下,不需要指定变量类型就可以构建LINQ表达式。
LINQ的数据源可以是数据库对象或是XML流等,也可以使实现了IEnumerable或者泛型IEnumberable<T>接口的集合对象。
LINQ的基本语法包含如下的8个上下文关键字,如下:
关键字 | 说明 |
from | 指定范围变量和数据源 |
where | 根据bool表达式从数据源中筛选数据 |
select | 指定查询结果中的元素所具有的类型或表现形式 |
group | 对查询结果按照键值进行分组(IGrouping<TKey,TElement>) |
into | 提供一个标识符,它可以充当对join、group或select子句结果的引用 |
order | 对查询出的元素进行排序(ascending/descending) |
join | 按照两个指定匹配条件来Equals连接两个数据源 |
let | 产生一个用于存储查询表达式中的子表达式查询结果的范围变量 |
1、From子句:
如果要写一个LINQ表达式,就必须是以from子句开头。
//单个form子句 string[] values = { "学习使我快乐", "学习使我幸福", "爸爸叫我吃饭", "我充耳不闻" }; var new_values = from value in values where value.IndexOf("学习") > -1 //查询以"学习"开头的元素 select new { value, value.Length }; foreach (var new_value in new_values) { Console.WriteLine("{0},{1}", new_value.value, new_value.Length); } Console.ReadKey(); //使用LINQ查询List集合 List<Person> personList = new List<Person>(); personList.Add(new Person { Name = "张三", Age = 20, Gender = '男' }); personList.Add(new Person { Name = "李四", Age = 21, Gender = '女' }); personList.Add(new Person { Name = "王五", Age = 22, Gender = '男' }); var new_personList = from Person person in personList where person.Age > 20 //查询年龄大于20的人 select person; foreach (Person new_person in new_personList) { Console.WriteLine("{0} 年龄:{1} 性别:{2}", new_person.Name, new_person.Age, new_person.Gender); } Console.ReadKey();
//复合from子句 List<Person> personList2 = new List<Person>() { new Person{ Name = "张三", Age = 20, Phone = new List<string>(){"666666","138******"}}, new Person{ Name = "李四", Age = 21, Phone = new List<string>(){"777777","138******"}}, new Person{ Name = "王五", Age = 22, Phone = new List<string>(){"888888","138******"}} }; //person、phone都是查询变量,作用域为当前查询语句 var new_personList2 = from person in personList2 from phone in person.Phone where phone.IndexOf("6666") > -1 //查询个人电话集合以"6666"开头的人 select person; foreach (var new_person in new_personList2) { Console.WriteLine("{0} 年龄{1}", new_person.Name, new_person.Age); foreach (var person_phone in new_person.Phone) { Console.WriteLine("电话:{0}", person_phone); } } Console.ReadKey(); //4多个from子句 var personList3 = from Person person in personList where person.Age > 21 //查询集合1年龄超过21的人 from Person person2 in personList2 where person2.Age == 21 //查询集合2年龄等于21的人 select new { person, person2 };//查询结果定制 foreach (var new_person in personList3) { Console.WriteLine("{0} {1}", new_person.person.Name, new_person.person2.Name); } Console.ReadKey();
运行结果如下:
2、Where子句:
where子句是LINQ表达式的元素筛选机制,除了开始和结束的位置,它几乎可以出现在LINQ表达式的任意位置上。在一个LINQ表达式中,可以有where子句,也可以没有;可以有一个,也可以有多个;多个where子句之间的逻辑关系相当于逻辑“与”,每个where子句可以包含1个或多个bool逻辑表达式,这些条件成为谓词,谓词逻辑之间用的是“&&”或“||”等而不是SQL中的and 、or。
//常见的where语句 List<Person> personList = new List<Person>() { new Person(){ Name = "张三三", Age = 20, Phone = "555555"}, new Person(){ Name = "李四", Age = 21, Phone = "666666"}, new Person(){ Name = "王五", Age = 22, Phone = "777777"} }; var new_personList = from person in personList where (person.Name.Length == 3 || person.Name.Substring(0, 1) == "李") && person.Age > 20 //查询姓名长度为3或姓名以"李"开头且年龄大于20的人 select new { person.Name, person.Age }; foreach (var new_person in new_personList) { Console.WriteLine("{0},{1}", new_person.Name, new_person.Age); } Console.ReadKey(); //在where子句中使用自定义函数 var new_personList2 = from Person person in personList where person.Name.Length == 2 && Check(person.Name) //查询姓名长度为2且符合自定义函数限定后的人 select person; foreach (var new_person in new_personList2) { Console.WriteLine("{0},{1},{2}", new_person.Name, new_person.Age, new_person.Phone); } Console.ReadKey(); //动态谓词的筛选 //定义动态谓词数组,在实际开发中可以动态获得 string[] names = { "李四", "XXX", "***", "@@@", "一些敏感词" }; var new_personList3 = from Person person in personList where !names.Contains(person.Name) //查询姓名不包含集合元素的人 select person; foreach (var new_person in new_personList3) { Console.WriteLine("{0} 年龄:{1},电话:{2}", new_person.Name, new_person.Age, new_person.Phone); } Console.ReadKey();
//自定义函数 static bool Check(string name) { if (name.Substring(0, 1) == "李") return false; return true; }
运行结果如下:
3、Select子句:
在select子句上可以非常灵活的处理查询到的元素,然后再把结果返回。
List<Person> personList = new List<Person>() { new Person(){ Name = "张三", Age = 20, Phone = "555555"}, new Person(){ Name = "李四", Age = 21, Phone = "666666"}, new Person(){ Name = "王五", Age = 22, Phone = "777777"} }; //常见的select语句 var new_personList = from person in personList where person.Age >= 22 && person.Age <= 30 //查询年龄大于等于22小于等于30的人 select new { person.Name, person.Age }; //处理查询结果,返回查到的人的姓名和年龄 foreach (var new_person in new_personList) { Console.WriteLine(new_person.Name + “ 年龄:” + new_person.Age); } Console.ReadKey(); //对查询到的结果集进行替换 var new_personList2 = from person in personList where person.Age >= 21 && person.Age <= 30 //查询年龄大于等于22小于等于30的人 select person.Name.Replace("李", "王"); //处理查询结果,返回查到的人的姓名替换后的结果 foreach (var new_person in new_personList2) { Console.WriteLine(new_person); } Console.ReadKey(); //在select子句中使用自定义函数 var new_personList3 = from person in personList where person.Age >= 20 && person.Age <= 21 //查询年龄大于等于20小于等于21的人 select MyDesc(person.Name); //处理查询结果,返回自定义函数处理后的人的姓名 foreach (var new_person in new_personList3) { Console.WriteLine(new_person); } Console.ReadKey(); //对查询结果进行投影 var new_personList4 = from person in personList where person.Age >= 21 && person.Age <= 22 //查询年龄大于等于21小于等于22的人 select new NewPerson { Name = "我的名字叫:" + person.Name, Age = person.Age + 1 }; //处理查询结果,返回投影结果 foreach (var new_person in new_personList4) { Console.WriteLine(new_person.Name + new_person.Age); } Console.ReadKey();
static string MyDesc(string s) { return s + "好帅"; }
public class NewPerson { public string Name { get; set; } public int Age { get; set; } }
运行结果如下:
4、Group子句:
LINQ表达式必须以from子句开头,以select或group子句结束,所以除了使用select来返回结果外,也可以使用group子句来返回元素分组后的结果。group子句返回的是一个基于IGrouping<TKey,TElement>泛型接口的对象序列。
注意:语法和SQL的group有点区别。
List<Person> personList = new List<Person>() { new Person(){ Name = "张一", Age = 20, Phone = "333333"}, new Person(){ Name = "张二", Age = 21, Phone = "444444"}, new Person(){ Name = "张三", Age = 22, Phone = "555555"}, new Person(){ Name = "李四", Age = 23, Phone = "666666"}, new Person(){ Name = "李五", Age = 24, Phone = "777777"}, new Person(){ Name = "赵六", Age = 25, Phone = "888888"}, new Person(){ Name = "田七", Age = 26, Phone = "999999"}, }; var new_personList = from person in personList group person by person.Name.Substring(0, 1); //按姓氏进行分组 //遍历键值和键值所属元素 foreach (IGrouping<string, Person> new_person in new_personList) { Console.WriteLine(); Console.WriteLine("姓:{0} ", new_person.Key); foreach (var p in new_person) { Console.WriteLine("{0} 年龄:{1} 电话:{2}", p.Name, p.Age, p.Phone); } } Console.ReadKey(); Console.WriteLine(); Console.WriteLine("-----------------------------------"); var new_personList2 = from person in personList group person by person.Age > 23; //按年龄是否大于23进行分组 foreach (IGrouping<bool, Person> new_person in new_personList2) { Console.WriteLine(); Console.WriteLine("年龄 {0} 23 ", new_person.Key ? "大于" : "小于"); foreach (var p in new_person) { Console.WriteLine("{0} 年龄:{1} 电话:{2}", p.Name, p.Age, p.Phone); } } Console.ReadKey();
运行结果如下:
5、Into子句:
into子句作为一个临时标识符,用于group、select、join子句中充当其结果的引用。
List<Person> personList = new List<Person>() { new Person(){ Name = "张一", Age = 20, Phone = "333333"}, new Person(){ Name = "张二", Age = 21, Phone = "444444"}, new Person(){ Name = "张三", Age = 12, Phone = "555555"}, new Person(){ Name = "李四", Age = 23, Phone = "666666"}, new Person(){ Name = "李五", Age = 14, Phone = "777777"}, new Person(){ Name = "赵六", Age = 25, Phone = "888888"}, new Person(){ Name = "田七", Age = 16, Phone = "999999"}, }; //into用于group子句 var new_personList = from person in personList group person by person.Name.Substring(0, 1) into person_group //相当于分组后的组名 select person_group; foreach (var persontGroup in new_personList) { Console.WriteLine("姓:{0} ", persontGroup.Key); foreach (var p in persontGroup) { Console.WriteLine("{0} 电话:{1}", p.Name, p.Phone); } } Console.ReadKey(); Console.WriteLine(); //select子句中的into子句 var new_personList2 = from person in personList select new { NewName = person.Name, NewAge = person.Age } into newperson //相当于创建一个新的对象 orderby newperson.NewAge select newperson; foreach (var persontGroup in new_personList2) { Console.WriteLine("{0} 年龄:{1}", persontGroup.NewName, persontGroup.NewAge); } Console.ReadKey();
运行结果如下:
6、OrderBy子句:
按照元素的一个或多个属性对元素进行排序。
List<Person> personList = new List<Person>() { new Person(){ Name = "张一", Age = 20, Phone = "333333"}, new Person(){ Name = "张二二", Age = 20, Phone = "444444"}, new Person(){ Name = "张三", Age = 12, Phone = "555555"}, new Person(){ Name = "李四", Age = 23, Phone = "666666"}, new Person(){ Name = "李五", Age = 14, Phone = "777777"}, new Person(){ Name = "赵六", Age = 25, Phone = "888888"}, new Person(){ Name = "田七", Age = 16, Phone = "999999"}, }; //按照年龄排序 var new_personList = from person in personList orderby person.Age //默认升序ascending,如果要降序要加上descending select person; foreach (var new_person in new_personList) { Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone); } Console.ReadKey(); Console.WriteLine(); //按照年龄进行降序排序,按照名字字数进行次要排序 var new_personList2 = from person in personList orderby person.Age descending, person.Name.Length descending select person; foreach (var new_person in new_personList2) { Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone); } Console.ReadKey();
运行结果如下:
7、Let子句:
let子句用于在LINQ表达式中存储子表达式的计算结果。
List<Person> personList = new List<Person>() { new Person(){ Name = "张一", Age = 20, Phone = "333333"}, new Person(){ Name = "张二二", Age = 20, Phone = "444444"}, new Person(){ Name = "张三", Age = 12, Phone = "555555"}, new Person(){ Name = "李四", Age = 23, Phone = "666666"}, new Person(){ Name = "李五", Age = 14, Phone = "777777"}, new Person(){ Name = "赵六", Age = 25, Phone = "888888"}, new Person(){ Name = "田七", Age = 16, Phone = "999999"}, }; //使用let子句创建范围变量g,并通过g构建查询表达式 var new_personList = from person in personList let g = person.Name.Substring(0, 1) where g == "张" || g == "李" select person; foreach (var new_person in new_personList) { Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone); } Console.ReadKey(); Console.WriteLine(); //也可以不使用let,上面的语句等效于下 var new_personList2 = from person in personList where person.Name.Substring(0, 1) == "张" || person.Name.Substring(0, 1) == "李" select person; foreach (var new_person in new_personList2) { Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone); } Console.ReadKey();
运行结果如下:
8、Join子句:
join子句能将两个数据源中元素可以进行相等比较的两个属性进行关联。使用equals关键字进行相等比较,而不是常用的双等号。
//定义两个数据源 List<Person> personList = new List<Person>() { new Person(){ Name = "张三", Age = 12, Phone = "555555"}, new Person(){ Name = "李四", Age = 23, Phone = "666666"}, new Person(){ Name = "王五", Age = 14, Phone = "777777"}, new Person(){ Name = "赵六", Age = 25, Phone = "888888"}, new Person(){ Name = "田七", Age = 16, Phone = "999999"}, }; List<PersonFile> personFileList = new List<PersonFile>() { new PersonFile(){ Name = "张三", File = "三好学生"}, new PersonFile(){ Name = "李四", File = "班长"}, new PersonFile(){ Name = "王五", File = "学习委员"}, new PersonFile(){ Name = "赵六", File = "差生"} }; //根据姓名进行内连接 var new_personList = from person in personList join file in personFileList on person.Name equals file.Name select new { Name = person.Name, file = file.File, Age = person.Age }; foreach (var new_person in new_personList) { Console.WriteLine("{0} {1} 年龄:{2}", new_person.Name, new_person.file, new_person.Age); } Console.ReadKey(); Console.WriteLine(); //等效于前面的多个from的效果 var new_personList2 = from person in personList from file in personFileList where person.Name == file.Name select new { Name = person.Name, File = file.File, Age = person.Age }; foreach (var new_person in new_personList2) { Console.WriteLine("{0} {1} 年龄:{2}", new_person.Name, new_person.File, new_person.Age); } Console.ReadKey(); Console.WriteLine(); //根据姓名进行分组连接 var new_personList3 = from person in personList join file in personFileList on person.Name equals file.Name into person_group select new { Name = person.Name, Files = person_group }; foreach (var new_person in new_personList3) { Console.WriteLine(new_person.Name); foreach (var p in new_person.Files) { Console.WriteLine(p.File); } } Console.ReadKey(); Console.WriteLine(); //根据姓名进行左外连接 var new_personList4 = from person in personList join file in personFileList on person.Name equals file.Name into person_group from position in person_group.DefaultIfEmpty() select new { Name = person.Name, File = position == null ? "无" : position.File }; foreach (var new_person in new_personList4) { Console.WriteLine("{0} {1} ", new_person.Name, new_person.File); } Console.ReadKey();
运行结果如下:
对Linq的基本子句用法学习和总结完毕,还有待继续练习达到灵活运行!