分享好友 编程语言首页 频道列表

一篇文章教你学会ASP.Net Core LINQ基本操作

ASP.NET  2023-02-08 20:140

一篇文章教你学会ASP.Net Core LINQ基本操作

为什么要使用LINQ

LINQ中提供了很多集合的扩展方法,配合lambda能简化数据处理。

例如我们想要找出一个IEnumerable<int>中所有大于10的元素,使用LINQ则可以这样写

static void Main(string[] args)
{
    int[] nums = new int[] { 3, 5, 6, 5, 10, 12, 14, 7 };
    IEnumerable<int> res = nums.Where(a => a > 10);
    foreach (int i in res)
        Console.WriteLine(i);
}

其中使用IEnumerableusing System.Collections.Generic;

使用Where方法要using System.Linq;,该方法会遍历每个元素然后去判断是否大于10

LINQ背后原理

为了解LINQ背后的原理,我们首先去实现一个简单的Where方法

第一种方案:

static IEnumerable<int> MyWhere1(IEnumerable<int> items, Func<int, bool> f)
{
    List<int> res = new List<int>();
    foreach (int i in items)
        if (f(i) == true)
            res.Add(i);
    return res;
}

第二种方案:

static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f)
{
    List<int> res = new List<int>();
    foreach (int i in items)
        if (f(i) == true)
            yield return i;
}

那么这两种方案的区别是什么?第一种方案是把所有元素全部检查一遍,把符合要求的元素放到List<int> res里面,然后返回res;然而第二种方案使用yield,是一种“流水线”方式处理,找到符合条件的元素立即返回,返回后Console.WriteLine立即能够打印,从而提高了数据处理效率。

LINQ的常用扩展方法

LINQ提供了很多扩展方法,大部分都在System.Linq命名空间中。

接下来准备一些数据,用于下面的操作。

首先定义一个员工类,里面有姓名工资等成员。

class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Gender { get; set; }
    public int Salary { get; set; }
    public override string ToString()
    {
        return $"ID={Id}, Name={Name}, Age={Age}, Gender={Gender}, Salary={Salary}";
    }

}

然后再Main方法中创建实例导入数据,并将所有实例保存到列表中。

List<Employee> lst = new List<Employee>();
lst.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 });
lst.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 });
lst.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 });
lst.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 });
lst.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 });
lst.Add(new Employee { Id = 6, Name = "nancy", Age = 35, Gender = false, Salary = 8000 });
lst.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 });
lst.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salary = 8000 });

Where方法

该方法会遍历每个元素然后去判断是否符合条件,符合条件的元素则被返回。

IEnumerable<Employee> res = lst.Where(e => e.Age > 20); //把年龄大于20的返回过来

foreach (Employee e in res)
    Console.WriteLine(e);

返回结果

ID=1, Name=jerry, Age=28, Gender=True, Salary=5000
ID=2, Name=jim, Age=33, Gender=True, Salary=3000
ID=3, Name=lily, Age=35, Gender=False, Salary=9000
ID=5, Name=kimi, Age=25, Gender=True, Salary=1000
ID=6, Name=nancy, Age=35, Gender=False, Salary=8000
ID=7, Name=zack, Age=35, Gender=True, Salary=8500
ID=8, Name=jack, Age=33, Gender=True, Salary=8000

Count方法

该方法会返回符合条件的元素的个数

Console.WriteLine(lst.Count(e=>e.Salary>8000));

返回结果

2

Any方法

该方法会判断是否存在至少一个元素符合条件。另外,如果传入的参数为空,则会判断IEnumerable(或者实现了IEnumerable接口的其他类,如List)是否存在元素。

如以下代码

List<int> test = new List<int>();
Console.WriteLine(test.Any());

由于列表为空,test.Any()返回的就是false

如果判断是否存在Employee类型的元素于lst中,则代码如下,其返回值为true

Console.WriteLine(lst.Any(e=>e.Salary>8000));

同样的,我们使用Count方法也可以达成此目的(判断返回元素个数是否为0),但是Count方法相对于Any方法效率较低

这是因为Any找到一个符合条件的元素会立即返回,而Count方法是统计个数,找到一个符合元素后还要继续向后找。

有关一条数据的方法

有关一条数据的方法有好几种,不过在细节上略有不一样,所以我们把他们放一块介绍。

方法 描述
Single() 有且只有一条满足要求的数据
SingleOrDefault() 最多只有一条满足要求的数据
First() 至少有一条,并且返回第一条
FirstOrDefault() 返回第一条或默认值

Signle方法

Employee elem = lst.Single(e => e.Salary > 8000);  //错误,有多条数据满足条件
Employee elem = lst.Single(e => e.Salary > 8500);	//正确,仅一条数据满足条件

SingleOrDefault方法

对于SingleOrDefault,当且仅当存在一条数据满足条件,返回该数据;如果存在多条则报错;如果不存在则返回默认值。

Employee elem = lst.SingleOrDefault(e=>e.Salary>8000);  //错误,有多条数据满足条件
Employee elem = lst.SingleOrDefault(e=>e.Salary>8500);	//正确,仅一条数据满足条件

下面我们来看下默认值的情况

int[] nums = new int[] { 1, 2, 3 };
int i = nums.SingleOrDefault(i => i > 10);
Console.WriteLine(i);

由于不存在大于10的整型数字,所以该方法返回变量i的默认值,输出结果为0

First方法

该方法要求数据至少有一条满足条件,并且返回查询到的第一条数据。

Employee test = lst.First(e=>e.Salary>9000);	//报错,不存在数据满足条件
//正确,满足年龄大于16的有多条,仅按照我们添加数据的顺序返回第一条
Employee test = lst.First(e=>e.Age>16);
Console.WriteLine(test);

输出结果

ID=1, Name=jerry, Age=28, Gender=True, Salary=5000

FirstOrDefault方法

该方法返回符合条件的第一条数据,否则返回默认值

如下方代码,从数组中返回一个大于2的整数,其输出结果为3

int[] nums = new int[] { 1, 2, 3, 4, 5, 6 };
int i = nums.FirstOrDefault(e => e > 2);
Console.WriteLine(i);

我们再来看看返回默认值的情况

int[] nums = new int[] { 1, 2, 3, 4, 5, 6 };
int i = nums.FirstOrDefault(e => e > 10);
Console.WriteLine(i);

由于数组中不存在大于10的数,所以i的值就是其默认值0

排序方法

一般排序

排序方法有两种

方法 描述
OrderBy() 正序排序
OrderByDescending() 逆序排序

二者用法几乎一致,此处仅演示OrderBy方法

IEnumerable<Employee> res2 = lst.OrderBy(e => e.Age);
foreach (Employee e in res2)
    Console.WriteLine(e);

其输出结果为

ID=4, Name=lucy, Age=16, Gender=False, Salary=2000
ID=5, Name=kimi, Age=25, Gender=True, Salary=1000
ID=1, Name=jerry, Age=28, Gender=True, Salary=5000
ID=2, Name=jim, Age=33, Gender=True, Salary=3000
ID=8, Name=jack, Age=33, Gender=True, Salary=8000
ID=3, Name=lily, Age=35, Gender=False, Salary=9000
ID=6, Name=nancy, Age=35, Gender=False, Salary=8000
ID=7, Name=zack, Age=35, Gender=True, Salary=8500

很显然数据已经按照年龄从小至大的顺序进行排序了。

此外应该注意的是,该方法必须有参数。如果想要对一个数组进行排序,正确写法如下

int[] nums2 = new int[] { 3, 1, 2, 4, 5, 6 };
IEnumerable<int> resNum = nums2.OrderBy(i => i);
//写成nums2.OrderBy()是错误的

多排序

所谓多排序,就是按照一个条件对数据进行排序后,存在多个数据该条件下的值一致,然后再对这些值一致的数据按照其他条件排序。

其方法也有两个,一般是在OrderBy()OrderByDescending()之后调用

方法 描述
ThenBy() 正序再排序
ThenByDescending() 逆序再排序

我们对文章上方的数据首先按照对年龄进行排序,然后对年龄一致的员工再按照工资进行逆排序,编写代码如下:

IEnumerable<Employee> sortTest = lst.OrderBy(x => x.Age).ThenByDescending(x => x.Salary);
foreach (Employee emp in sortTest)
    Console.WriteLine(emp);

输出如下:

ID=4, Name=lucy, Age=16, Gender=False, Salary=2000
ID=5, Name=kimi, Age=25, Gender=True, Salary=1000
ID=1, Name=jerry, Age=28, Gender=True, Salary=5000
ID=8, Name=jack, Age=33, Gender=True, Salary=8000
ID=2, Name=jim, Age=33, Gender=True, Salary=3000
ID=3, Name=lily, Age=35, Gender=False, Salary=9000
ID=7, Name=zack, Age=35, Gender=True, Salary=8500
ID=6, Name=nancy, Age=35, Gender=False, Salary=8000

限制结果集的方法

限制结果集,获取部分数据的方法一般是利用SkipTake方法

例如我想要从上述员工数据中,获取从第2条开始连续的3条数据,则代码可以这样写;

IEnumerable<Employee> sortTest = lst.Skip(2).Take(3);
foreach (Employee emp in sortTest)
    Console.WriteLine(emp);

其输出结果如下:

ID=3, Name=lily, Age=35, Gender=False, Salary=9000
ID=4, Name=lucy, Age=16, Gender=False, Salary=2000
ID=5, Name=kimi, Age=25, Gender=True, Salary=1000

当然,Skip方法和Take方法也可以单独使用:

Take单独使用

IEnumerable<Employee> sortTest = lst.Take(3);
foreach (Employee emp in sortTest)
    Console.WriteLine(emp);

输出:

ID=1, Name=jerry, Age=28, Gender=True, Salary=5000
ID=2, Name=jim, Age=33, Gender=True, Salary=3000
ID=3, Name=lily, Age=35, Gender=False, Salary=9000

Skip单独使用

IEnumerable<Employee> sortTest = lst.Skip(2);
foreach (Employee emp in sortTest)
    Console.WriteLine(emp);

输出:

ID=3, Name=lily, Age=35, Gender=False, Salary=9000
ID=4, Name=lucy, Age=16, Gender=False, Salary=2000
ID=5, Name=kimi, Age=25, Gender=True, Salary=1000
ID=6, Name=nancy, Age=35, Gender=False, Salary=8000
ID=7, Name=zack, Age=35, Gender=True, Salary=8500
ID=8, Name=jack, Age=33, Gender=True, Salary=8000

聚合函数(方法)

LINQ聚合函数常用的有这些,但应当注意的是它们的返回值类型不与其他LINQ的方法一样是IEnumerable而是条件的值的类型

方法 描述
Max() 返回给定条件的最大值
Min() 返回给定条件的最小值
Average() 返回给定条件的平均值
Sum() 返回给定条件的和
Count() 统计满足条件的数据的个数

这些方法用法大致相同,甚至Count方法在上文中已经介绍过,此处仅用Max方法演示

int maxSalary = lst.Max(x => x.Salary);
Console.WriteLine(maxSalary);

该样例会输出所有员工的最大工资(请注意maxSalary的类型)

如果想要找到大于30岁的员工的最高工资,则可以

int maxSalary = lst.Where(x=>x.Age > 30).Max(x => x.Salary);

GroupBy方法

该方法用于对数据分组,其参数是分组条件表达式,返回值为IGrouping<TKey, TSource>类型的泛型IEnumerable。

我们编写代码来实现很具年龄分组:

IEnumerable<IGrouping<int, Employee>> items = lst.GroupBy(x => x.Age);
foreach (var item in items)
{
    Console.WriteLine($"年龄为{item.Key}的分组成员有:");
    foreach (var i in item)
        Console.WriteLine(i);
    Console.WriteLine();
}

其输出结果为:

年龄为28的分组成员有:
ID=1, Name=jerry, Age=28, Gender=True, Salary=5000

年龄为33的分组成员有:
ID=2, Name=jim, Age=33, Gender=True, Salary=3000
ID=8, Name=jack, Age=33, Gender=True, Salary=8000

年龄为35的分组成员有:
ID=3, Name=lily, Age=35, Gender=False, Salary=9000
ID=6, Name=nancy, Age=35, Gender=False, Salary=8000
ID=7, Name=zack, Age=35, Gender=True, Salary=8500

年龄为16的分组成员有:
ID=4, Name=lucy, Age=16, Gender=False, Salary=2000

年龄为25的分组成员有:
ID=5, Name=kimi, Age=25, Gender=True, Salary=1000

通过打印我们可以发现IEnumerable元素为IGrouping类型,其键与值对应关系是一对多的。在这里每个元素的键就是年龄,而值为具有相同年龄的Employee类型的员工数据。

投影与匿名类型

投影是把集合中每一项转化为另外一种类型。

IEnumerable<string> items = lst.Where(x => x.Salary > 5000).Select(x => x.Gender ? "男" : "女");
foreach (var item in items)
    Console.WriteLine(item);

输出结果为:

女
女
男
男

匿名类型没有名称,所以我们没有办法去用类型名去声明它,而是需要用到var关键字

var items = lst.Select(e => new
{
    XingMing = e.Name,
    NianLing = e.Age,
    Xingbie = e.Gender ? "男" : "女"
});
foreach (var item in items)
    Console.WriteLine(item);

其输出结果为:

{ XingMing = jerry, NianLing = 28, Xingbie = 男 }
{ XingMing = jim, NianLing = 33, Xingbie = 男 }
{ XingMing = lily, NianLing = 35, Xingbie = 女 }
{ XingMing = lucy, NianLing = 16, Xingbie = 女 }
{ XingMing = kimi, NianLing = 25, Xingbie = 男 }
{ XingMing = nancy, NianLing = 35, Xingbie = 女 }
{ XingMing = zack, NianLing = 35, Xingbie = 男 }
{ XingMing = jack, NianLing = 33, Xingbie = 男 }

类型转换

在实际使用中,我们往往不是一定用IEnumerable,还有可能是List等,所以需要用到类型转换

例如我们利用Where方法返回工资大于6000的员工存放到IEnumerable中,然后将其转化为List类型

List<Employee> lst2 = lst.Where(e => e.Salary > 6000).ToList();

此外还有ToArray等方法,此处不过多说明。

链式调用

所谓链式调用就是调用完一个函数(方法)后还能再后面继续跟着调用其它函数(方法)。

由于LINQ绝大多数方法返回的都是IEnumerable方法,而且绝大部分都是针对IEnumerable接口,所以可以在调用方法后继续调用其他方法。

lst.Where(e => e.Salary > 6000).ToList();

例如我们定义数组

int[] nums = new int[] { 1, 2, 3, 4, 5, 6 };

在小于3的元素中选取最大值

int a = nums.Where(x=>x<3).Max();

对于上面这行nums.Where(x=>x<3).Max()Where方法后加一个点然后再调用Max方法的形式就叫做链式调用。

查询语法

对于上述的使用WhereSelect等扩展方法进行数据查询的写法叫做LINQ方法语法

然而还有一种叫做查询语法

我们同样定义一个数组演示

int[] nums = new int[] { 6,5,4,3,2,1 };

我们取小于3的元素,然后进行正序排序,则用查询语法则可以如下:

var items = from e in nums
            where e < 3
            orderby e
            select e;

这里需要注意,查询语法需要以select或group子句结尾

那么问题来了,方法语法与查询语法有什么区别?我们可以用方法语法写一段相同效果的语法,然后用反编译器(ILSpy)去看一下代码。

其反编译结果给出了查询语法的形式,然后对查询语法生成文件进行反编译,发现结果相同,这说明两种方法在编译后没有任何区别只是写法不同

结束

LINQ的基本操作大致就这些,感谢杨中科老师提供的课程

查看更多关于【ASP.NET】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
ASP.NET操作Cookies的问题(Bug or Not)
以下存和取都是在不同的页面中,如果是在同一个页面也没必要用cookies了。 Test1: 给Cookies赋值: const string AAA="aaa"; Response.Cookies[AAA].Value = "111;222;333"; 取值: string value = Request.Cookies[AAA].Value; // value为111 Test2: 给Cooki

0评论2023-02-09888

Asp.Net Core 自定义验证属性
  很多时候,在模型上的验证需要自己定义一些特定于我们需求的验证属性。所以这一篇我们就来介绍一下怎么自定义验证属性。  我们来实现一个验证邮箱域名的自定义验证属性,当然,最重要的是需要定义一个继承自ValidationAttribute的类,然后在实现其IsVal

0评论2023-02-09525

Asp.Net 之 枚举类型的下拉列表绑定
有这样一个学科枚举类型:/// 学科 /// /summary public enum Subject {None = 0,[Description("语文")]Chinese = 1,[Description("数学")]Mathematics = 2,[Description("英语")]English = 3,[Description("政治")]Politics = 4,[Description("物理&qu

0评论2023-02-09819

[ASP.NET笔记] 1.Web基础知识
     1:http协议:     2:web服务器:     3:静态网页的概念     4:动态网页的概念       http协议:http(hypertext transfer protocol) 即超文本传输协议,这个协议是在internet上进行信息传送的协议任何网页之间要相互沟通,必须要尊循

0评论2023-02-09663

ASP.NET邮件发送 .net 发送邮件
  今天做了个ASP.NET做发送邮件功能,发现QQ邮箱好奇怪,当你用QQ邮箱做服务器的时候什么邮件都发送不出去(QQ邮箱除外)。而且爆出这样的错误:"邮箱不可用。 服务器响应为: Error: content rejected.http://mail.qq.com/zh_CN/help/content/rejectedmail.ht

0评论2023-02-09455

由ASP.NET Core根据路径下载文件异常引发的探究
前言    最近在开发新的项目,使用的是ASP.NET Core6.0版本的框架。由于项目中存在文件下载功能,没有使用类似MinIO或OSS之类的分布式文件系统,而是下载本地文件,也就是根据本地文件路径进行下载。这其中遇到了一个问题,是关于如何提供文件路径的,通

0评论2023-02-09562

ASP.NET 后台接收前台POST过来的json数据方法
 ASP.NET前后台交互之JSON数据 https://www.cnblogs.com/ensleep/p/3319756.html

0评论2023-02-09501

Asp.Net 常用工具类之加密——对称加密DES算法(2)
     又到周末,下午博客园看了两篇文章,关于老跳和老赵的程序员生涯,不禁感叹漫漫程序路,何去何从兮!  转眼毕业的第三个年头,去过苏州,跑过上海,从一开始的凌云壮志,去年背起行囊默默回到了长沙准备买房,也想有个家(毕竟年级不小了),有盼

0评论2023-02-09995

把自己的ASP.NET应用程序与SharePoint集成在一起
微软有文档描述这个解决方法的, 如下.   How to enable an ASP.Net application to run on a SharePoint virtual server http://support.microsoft.com/kb/828810?&clcid=0x409 Allowing Web Applications to Coexist with Windows SharePoint Services http

0评论2023-02-09749

ASP.NET中的OutOfMemoryException
在博客园看到了一位园友写的文章《如何处理OutOfMemoryException异常?》,于是想和大家交流一下ASP.NET中出现OutOfMemoryException的问题。实际上,在ASP.NET Web服务器上,ASP.NET所能够用到的内存,通常不会等同于所有的内存数量。在machine.config配置文

0评论2023-02-09823

【Mono】Linux下的Asp.Net配置指南
本文将介绍如何在Linux操作系统上搭建Asp.Net服务,在阅读本文之前,读者要先确 定自己已经安装好Linux操作系统、Mono Runtime,假如需要使用mod_mono的话,还 需要首先完成Apache的安装和配制。Mono的安装和配置请参考这里。 XSP XSP是一个轻量级的Web服务器

0评论2023-02-09312

EXT调用ASP.NET AJAX WebService
Posted 周五, 04/11/2008 - 16:34 by admin 在asp.net ajax中,使客户端调用WebService变得非常的简单,而且非常有用(个人觉得这个功能是asp.net ajax的核心,很多与客户端的交互都需要这个功能来辅助实现)。那在EXT中,标准的客户端与服务器端交互,使用的

0评论2023-02-09499

asp.net中Timer定时器在web中无刷新的使用
最近在做一个项目的时候,web端的数据需要与数据源进行实时同步,并保证数据的准确性,当时,考虑到使用ajax异步刷新技术。但后来在网上查找相关资料时,发现这样做,太浪费资源了,因为ajax的提交请求不应该这么频繁的,只适用于那种手动请求响应的那种,因

0评论2023-02-09961

ASP.Net访问母版页(MasterPage)控件、属性、方法及母版页中调用内容页的方法
总结了一下ASP.Net访问母版页控件、属性、方法及母版页中调用内容页的方法,供大家参考: 首先,必须通过内容页中的MasterTye指令,对母版页实施强类型化,即在内容页代码头的设置中增加如下指令%@ MasterType VirtualPath="~/Master/MenuElement.master" %

0评论2023-02-09783

asp.net 2.0 ajax中使用PopupControlExtender
 最近在著名的4guysfromrolla.com(http://www.4guysfromrolla.com)上,有篇不错的文章(http://aspnet.4guysfromrolla.com/articles/070407-1.aspx),讲的是如何使用aspajx中的controltookit中的PopupControlExtender控件来实现一些特殊的效果,比如文中举了

0评论2023-02-09965

更多推荐