[.net 面向对象编程基础] (20) LINQ使用

[.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操作语法

LINQ查询时有两种语法可供选择:查询表达式(Query Expression)和方法语法(Fluent Syntax)

.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子句起到的作用是将前面语句的结果作为后面语句操作的数据源

3. LINQ to Objects

LINQ to Objects是LINQ的精华部分。

LINQ to Objects 提供对内存中集合操作的支持,由程序集System.Core.dll中System.Linq命名空间下的Enumerable静态类提供。

这些扩展方法都是针对IEnumerable的对象进行扩展的也就是说,只要实现了IEnumerable接口,就可以使用这些扩展方法。

这些扩展方法都是针对IEnumerable的对象进行扩展的也就是说,只要实现了IEnumerable接口,就可以使用这些扩展方法。

4.示例

我们说对于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();

输出结果如下:

4.2.  投影操作符

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();

输出结果如下:

<未完待续。。。。小伙伴们,随后继续给几种实例。。。>

==============================================================================================

返回目录 <如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>

==============================================================================================

时间: 2024-10-24 16:23:23

[.net 面向对象编程基础] (20) LINQ使用的相关文章

[.net 面向对象编程基础] (21) 委托

[.net 面向对象编程基础] (20)  委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1.什么是委托? 学习委托,我想说,学会了就感觉简单的不能再简单了,没学过或都不愿了解的人,看着就头大,其实很简单.委托在.net面向对象编程和学习设计模式中非常重要,是学习.net面向对象编程必须要学会并掌握的. 委托从字面上理解,就是把做一些事情交给别人来帮忙完成.在C#中也可以这样理解,委托就是动态调用方法.这样说明,就很好理解了. 平时我们会

[.net 面向对象编程基础] (22) 事件

[.net 面向对象编程基础] (22)  事件 事件(Event)是学习.net面向对象编程很重要的一部分,在学习事件之前,我们实际上已经在很多地方使用了事件,比如控件的click事件等,这些都是.net设计控件的时候已经定义好的事件.除此之外,我们同样可以自己定义事件. 事件实际上是一种消息机制,当然点击控件时,click就通知处理他的方法去处理,实际上就是前面说的委托.因此我们可以说:事件是一种具有特殊签名的委托.而事件/消息机制是windows的核心,因此我们必须掌握他. 为了更加容易理

[.net 面向对象编程基础] (14) 重构

[.net 面向对象编程基础] (14) 重构 通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然了,这也是从一个普通程序员到一个高级程序员的必由之路.就看病一样,普通医生只能治标,高级医生不但看好病,还能除病根. 1.什么时重构? 重构(Refactoring)就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量.性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性.

[.net 面向对象编程基础] (17) 数组与集合

[.net 面向对象编程基础] (17) 数组与集合 学习了前面的C#三大特性,及接口,抽象类这些相对抽象的东西以后,是不是有点很累的感觉.具体的东西总是容易理解,因此我们在介绍前面抽象概念的时候,总是举的是具体的实例以加深理解. 本节内容相当具体,学起来也相当轻松. 1.数组 1.1 什么是数组? 数组是一种数据结构,包含同一个类型的多个元素. 1.2数组的初始化 string[] mystringArray; 类型+方框号 数组名 1.3数组初始化 我们知道数组是引用类型,所以需要给他分配堆

[.net 面向对象编程基础] (16) 接口

[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目的一就是让第三方厂商生产的外设都有相同的标准,也是提供一个对外通信或操作的入口. 只是C#的接口除了以上特点之外,还具有一种类似于模板的功能,我们定义一组接口,就像是一个模板.这点和抽象类不同,抽象类是先有子类或都子类的概念,从中抽象出一个类.而接口更像是我们要设计一台机器,先把这台机器对外的功能接

[.net 面向对象编程基础] (18) 泛型

[.net 面向对象编程基础] (18) 泛型 上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检索不方便.集合检索和声明方便,但是存在类型安全问题,本来使一个类型安全的C#变得不安全了. 集合为了解决数组预设大小的问题,采取了一种自动扩容的办法,这样当大小不够时,他就创建一个新的存储区域,把原有集合的元素复制过来.如此又对性能上也是有很大的影响. 上节我们说到解决这些缺陷的方法,那就是.NET

[.net 面向对象编程基础] (15) 抽象类

[.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义为一个虚方法或抽象方法.这节单独把抽象类提出来,是因为抽象是.net实现面向对象编程重要的重要思想,定义抽象类就象一个模板一个提纲,它可以理解为中央的指导思想,我们通过派生类去具体实现它.由此可见,抽象类本身没有任何作用,也不能被实例化,因为他本身就不具有具体的事物.比如上节的动物类的例 子,我们实

[.net 面向对象编程基础] (11) 面向对象三大特性——封装

[.net 面向对象编程基础] (11) 面向对象三大特性——封装 我们的课题是面向对象编程,前面主要介绍了面向对象的基础知识,而从这里开始才是面向对象的核心部分,即 面向对象的三大特性:封装.继承.多态. 1.封装概念 封装:每个对象都包含有它能进行操作的所有信息,这个特性称为封装.这样的方法包含在类中,通过类的实例来实现. 2.封装的优点 A.良好的封装能够减少耦合(比如实现界面和逻辑分离) B.可以让类对外接口不变,内部可以实现自由的修改 C.类具有清晰的对外接口,使用者只需调用,无需关心

[.net 面向对象编程基础] (7) 基础中的基础——流程控制语句

[.net 面向对象编程基础] (7) 基础中的基础——流程控制语句 本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些,本节参考了网上朋友所写的例子,为的是让更多小伙伴学习,提高,加薪,如有版权问题,请邮件我,我第一时间处理. 语句:是程序中的小指令,本节主要以流程控制语句为主要内容. 流程控制语句中最常用的三个是 选择语句(即条件语句).循环语句和异常处理语句 流程控制语句分类: 类别    关键字 选择语句  if.else.switch.case 循环语句  d