[.net 面向对象编程基础 ] ( 20) LINQ 使用
通过上节 LINQ 的基础知识的学习,我们可以开始使用 LINQ 来进行内存数据的查询了,我们上节说了 LINQ 的定义为: Language Integrated Query (语言集成查询)的简称,它是集成在 .NET 编程语言中的一种特性 .
1.LINQ 的构架
从这幅图中,我们可以知道 LINQ包括五个部分:LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to DataSet、LINQ to Entities 。
程序集 | 命名空间 | 描述 | |
LINQ to Objects | System.Core.dll | System.Linq | 提供对内存中集合操作的支持 |
LINQ to XML | System.Xml.Linq.dll | System.Xml.Linq | 提供对XML数据源的操作的支持 |
LINQ to SQL | System.Data.Linq.dll | System.Data.Linq | 提供对Sql Server数据源操作的支持。(微软已宣布不再更新,推荐使用LINQ to Entities) |
LINQ to DataSet | System.Data.DataSetExtensions.dll | System.Data | 提供对离线数据操作的支持。 |
LINQ to Entities | System.Core.dll 和System.Data.Entity.dll | System.Linq 和 System.Data.Objects | LINQ to Entities 是 Entity Framework 的一部分并且取代LINQ to SQL 作为在数据库上使用 LINQ 的标准机制。(Entity Framework 是由微软发布的开源对象-关系映射(ORM)框架,支持多种数据库。) |
目前,还可以下载其他第三方提供程序,例如 LINQ to JSON 、LINQ to MySQL、LINQ to Amazon、LINQ to Flickr和LINQ to SharePoint。无论使用什么数据源,都可以通过LINQ使用相同的API进行操作。
1.LINQ 操作语法
.NET公共语言运行库(CLR)并不具有查询表达式的概念。所以,编译器会在程序编译时把查询表达式转换为方法语法,即对扩展方法的调用。所以使用方法语法会让我们更加接近和了解LINQ的实现和本质,并且一些查询只能表示为方法调用。但另一方面,查询表达式通常会比较简单和易读。不管怎样,这两种语法是互相补充和兼容的,我们可以在一个查询中混合使用查询表达式和方法语法。
.net 的设计者在类库中定义了一系列的扩展方法来方便用户操作集合对象 ,这些扩展方法构成了 LINQ 的查询操作符 。
以下扩展方法存在对应的查询表达式关键字:Where、Select、SelectMany、OrderBy、ThenBy、OrderByDescending、ThenByDescending、GroupBy、Join、GroupJoin。
LINQ查询表达式
约束 | LINQ查询表达式必须以from子句开头,以select或group子句结束。 |
关键字 | 功能 |
from…in… | 指定要查找的数据源以及范围变量,多个from子句则表示从多个数据源查找数据。 注意:c#编译器会把“复合from子句”的查询表达式转换为SelectMany()扩展方法。 |
join…in…on…equals… | 指定多个数据源的关联方式 |
let | 引入用于存储查询表达式中子表达式结果的范围变量。通常能达到层次感会更好,使代码更易于阅读。 |
orderby、descending | 指定元素的排序字段和排序方式。当有多个排序字段时,由字段顺序确定主次关系,可指定升序和降序两种排序方式 |
where | 指定元素的筛选条件。多个where子句则表示了并列条件,必须全部都满足才能入选。每个where子句可以使用谓词&&、||连接多个条件表达式。 |
group | 指定元素的分组字段。 |
select | 指定查询要返回的目标数据,可以指定任何类型,甚至是匿名类型。(目前通常被指定为匿名类型) |
into | 提供一个临时的标识符。该标识可以引用join、group和select子句的结果。 1) 直接出现在join子句之后的into关键字会被翻译为GroupJoin。(into之前的查询变量可以继续使用) 2) select或group子句之后的into它会重新开始一个查询,让我们可以继续引入where, orderby和select子句,它是对分步构建查询表达式的一种简写方式。(into之前的查询变量都不可再使用) |
表达式的语法如下:
from [type] id in source
[join [type] id in source on expr equals expr [into subGroup]]
[ from [type] id in source | let id = expr | where condition]
[ orderby ordering,ordering,ordering...]
select expr | group expr by key
[into id query]
<1> 第一行:
type 是可选的, id 是集合中的一项, source 是一个集合,如果集合中的类型与 type 指定的类型不同则导致强制转化
<2> 第二行:
一个查询表达式中可以有 0 个或多个 join 子句,
这里的 source 可以不等于第一句中的source
expr 可以是一个表达式
[into subGroup] subGroup 是一个中间变量,它继承自 IGrouping ,代表一个分组,也就是说“一对多”里的“多”
可以通过这个变量得到这一组包含的对象个数,以及这一组对象的键
<3> 第三行:
一个查询表达式中可以有 1 个或多个 from 子句
一个查询表达式中可以有 0 个或多个 let 子句, let 子句可以创建一个临时变量
一个查询表达式中可以有 0 个或多个 where 子句, where 子句可以指定查询条件
<4> 第四行:
一个查询表达式可以有 0 个或多个排序方式
每个排序方式以逗号分割
<5> 第五行:
一个查询表达式必须以 select 或者 group by 结束
select 后跟要检索的内容
group by 是对检索的内容进行分组
<6> 第六行:
最后一个 into 子句起到的作用是将前面语句的结果作为后面语句操作的数据源
LINQ to Objects 是 LINQ 的精华部分。
LINQ to Objects 提供对内存中集合操作的支持,由程序集 System.Core.dll 中 System.Linq 命名空间下的 Enumerable 静态类提供。
这些扩展方法都是针对 IEnumerable 的对象进行扩展的也就是说,只要实现了 IEnumerable 接口,就可以使用这些扩展方法 。
这些扩展方法都是针对 IEnumerable 的对象进行扩展的也就是说,只要实现了 IEnumerable 接口,就可以使用这些扩展方法 。
我们说对于 LINQ 查询语法, 有两种可供选择,一种是扩展方法(又叫方法语法( Fluent Syntax ) ),另一种是查询表达式( Query Expression ) 。他们是等价的,只是写法上不同。
在下面的示例中,我们都会用两种语法来完成。
编号 Id | 姓名 Name | 年龄 Age | 门派 Menpai | 武学 Kungfu | 武学级别 Level |
1 | 黄蓉 | 18 | 丐帮 | 打狗棒法 | 9 |
2 | 洪七公 | 70 | 丐帮 | 打狗棒法 | 10 |
3 | 郭靖 | 22 | 丐帮 | 降龙十八掌 | 10 |
4 | 任我行 | 50 | 明教 | 葵花宝典 | 1 |
5 | 东方不败 | 35 | 明教 | 葵花宝典 | 10 |
6 | 林平之 | 23 | 华山 | 葵花宝典 | 7 |
7 | 岳不群 | 50 | 华山 | 葵花宝典 | 8 |
编号 KongfuId | 武学名称 KongfuName | 杀伤力 Lethality |
1 | 打狗棒法 | 90 |
2 | 降龙十八掌 | 95 |
3 | 葵花宝典 | 100 |
如上对象“武林高手”( MartialArtsMaster )和“武学”( Kongfu )
1 /// <summary> 2 /// 类:武林高手 3 /// MartialArtsMaster 4 /// </summary> 5 class MartialArtsMaster 6 { 7 /// <summary> 8 /// 编号 9 /// </summary> 10 public int Id{get;set;} 11 /// <summary> 12 /// 姓名 13 /// </summary> 14 public string Name { get; set; } 15 /// <summary> 16 /// 年龄 17 /// </summary> 18 public int Age { get; set; } 19 /// <summary> 20 /// 门派 21 /// </summary> 22 public string Menpai { get; set; } 23 /// <summary> 24 /// 武学 25 /// </summary> 26 public string Kungfu { get; set; } 27 /// <summary> 28 /// 级别 29 /// </summary> 30 public int Level { get; set; } 31 } 32 33 /// <summary> 34 /// 类:武学 35 /// Kongfu 36 /// </summary> 37 class Kongfu 38 { 39 /// <summary> 40 /// 武学编号 41 /// </summary> 42 public int KongfuId { get; set; } 43 44 /// <summary> 45 /// 武学名称 46 /// </summary> 47 public string KongfuName { get; set; } 48 49 /// <summary> 50 /// 杀伤力 51 /// </summary> 52 public int Lethality { get; set; } 53 }
//初始化武林高手 var master = new List<MartialArtsMaster>(){ new MartialArtsMaster(){ Id = 1, Name = "黄蓉", Age = 18, Menpai = "丐帮", Kungfu = "打狗棒法", Level = 9 }, new MartialArtsMaster(){ Id = 2, Name = "洪七公", Age = 70, Menpai = "丐帮", Kungfu = "打狗棒法", Level = 10 }, new MartialArtsMaster(){ Id = 3, Name = "郭靖", Age = 22, Menpai = "丐帮", Kungfu = "降龙十八掌",Level = 10 }, new MartialArtsMaster(){ Id = 4, Name = "任我行", Age = 50, Menpai = "明教", Kungfu = "葵花宝典", Level = 1 }, new MartialArtsMaster(){ Id = 5, Name = "东方不败",Age = 35, Menpai = "明教", Kungfu = "葵花宝典", Level = 10 }, new MartialArtsMaster(){ Id = 6, Name = "林平之", Age = 23, Menpai = "华山", Kungfu = "葵花宝典", Level = 7 }, new MartialArtsMaster(){ Id = 7, Name = "岳不群", Age = 50, Menpai = "华山", Kungfu = "葵花宝典", Level = 8 } }; //初始化武学 var kongfu = new List<Kongfu>(){ new Kongfu(){KongfuId=1, KongfuName="打狗棒法", Lethality=90}, new Kongfu(){KongfuId=2, KongfuName="降龙十八掌", Lethality=95}, new Kongfu(){KongfuId=3, KongfuName="葵花宝典", Lethality=100}
4.1 过滤操作符
根据条件返回匹配元素的集合IEnumerable<T>。
1) Where:根据返回bool值的Func委托参数过滤元素。
业务说明:查询获得车手冠军次数大于15次且是Austria国家的一级方程式赛手
2) OfType<TResult>:接收一个非泛型的IEnumerable集合,根据OfType泛型类型参数过滤元素,只返回TResult类型的元素。
业务说明:过滤object数组中的元素,返回字符串类型的数组。
3) Distinct:删除序列中重复的元素。
示例一:查询 丐帮 中 修行 " 级别 " 高于 "8 级 " 的大侠
1 //示例一:查询 丐帮 中 修行"级别"高于 "8级" 的大侠 2 3 //表达式 写法 4 var GaiBangMaster = from m in master 5 where m.Level > 8 && m.Menpai == "丐帮" 6 select m; 7 8 //扩展方法 写法 9 var GaiBangMasterMethod = master.Where(m => m.Level > 8 && m.Menpai == "丐帮"); 10 11 //输出结果 12 string GaiBangMasterResult="查询/"丐帮/"中/"功力/"高于80的大侠(表达式写法):/n"; 13 GaiBangMasterResult +="编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)/n"; 14 15 foreach(var m in GaiBangMaster) 16 GaiBangMasterResult += m.Id + " " + m.Name+" "+m.Age+" "+m.Menpai+" "+m.Kungfu+" "+m.Level+" " + "/n"; 17 Console.WriteLine(GaiBangMasterResult); 18 19 string GaiBangMasterMethodResult = "查询/"丐帮/"中/"功力/"高于80的大侠(扩展方法 写法):/n"; 20 GaiBangMasterMethodResult += "编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)/n"; 21 22 foreach (var m in GaiBangMasterMethod) 23 GaiBangMasterMethodResult += m.Id + " " + m.Name + " " + m.Age + " " + m.Menpai + " " + m.Kungfu + " " + m.Level + " " + "/n"; 24 Console.WriteLine(GaiBangMasterMethodResult); 25 26 Console.ReadKey();
输出结果如下:
1) Select 将序列的每个元素经过lambda表达式处理后投影到一个新类型元素上。(与SelectMany不同在于,若单个元素投影到IEnumerable<TResult>,Select不会对多个IEnumerable<TResult>进行合并)
2) SelectMany
a) c#编译器会把“复合from子句”的查询表达式转换为SelectMany()扩展方法。
b) 将序列的每个元素经过lambda表达式处理后投影到一个 IEnumerable<TResult>,再将多个IEnumerable<TResult>序列合并为一个返回序列IEnumerable<TResult>。
c) 将序列的每个元素经过lambda表达式处理后投影到一个 IEnumerable<TCollection>,再将多个IEnumerable<TCollection>序列合并为一个返回序列IEnumerable<TCollection>,并对其中每个元素调用结果选择器函数。
示例二:过滤所学”武功” “伤杀力” 大于 90 的大侠
1 //示例二:过滤所学”武功” “伤杀力” 大于90 的大侠 2 //表达式 写法 3 var masterKongfu = from m in master 4 from k in kongfu 5 where( k.Lethality > 90 && m.Kungfu==k.KongfuName) 6 orderby m.Level 7 select m.Id + " " + m.Name + " " + m.Age + " " + m.Menpai + " " + m.Kungfu + " " + m.Level + " "; 8 //扩展方法 写法 9 var masterKongfuMethod = master 10 .SelectMany( 11 k => kongfu, 12 (m, k) => new { mt = m, kf = k } 13 ) 14 .Where(x =>x.kf.Lethality> 90 && x.mt.Kungfu==x.kf.KongfuName ) 15 .OrderBy(m => m.mt.Level) 16 .Select(m => m.mt.Id + " " + m.mt.Name + " " + m.mt.Age + " " + m.mt.Menpai + " " + m.mt.Kungfu + " " + m.mt.Level + " "); 17 //输出结果 18 string masterKongfuResult = "过滤所学”武功” “伤杀力” 大于90 的大侠(表达式写法):/n"; 19 masterKongfuResult += "编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)/n"; 20 foreach (var m in masterKongfu) 21 masterKongfuResult += m.ToString()+" " + "/n"; 22 Console.WriteLine(masterKongfuResult); 23 24 string masterKongfuMethodResult = "过滤所学”武功” “伤杀力” 大于90 的大侠(扩展方法 写法):/n"; 25 masterKongfuMethodResult += "编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)/n"; 26 foreach (var m in masterKongfuMethod) 27 masterKongfuMethodResult += m.ToString() + " " + "/n"; 28 Console.WriteLine(masterKongfuMethodResult); 29 30 Console.ReadKey();
输出结果如下:
<未完待续。。。。小伙伴们,随后继续给几种实例。。。>
==============================================================================================
返回目录
<如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>==============================================================================================