前言

因为咱们的.NET EF Core快速入门实战教程经常会用到 LINQ 去查询和操作 MySQL 中的数据,因此我觉得很有必要对 LINQ 的一些使用技巧、常用方法、特性做一个详细的介绍,让大家在后续的课程中不迷茫。

LINQ介绍

LINQ语言集成查询是一系列直接将查询功能集成到 C# 语言的技术统称。数据查询历来都表示为简单的字符串,没有编译时类型检查或 IntelliSense 支持。此外,需要针对每种类型的数据源了解不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等。然而,LINQ的出现改变了这一现状,它使查询成为了与类、方法和事件同等重要的高级语言构造。通过LINQ,开发者能够以声明性的方式查询和操作数据,极大地提高了开发效率和代码的可维护性。

LINQ具有以下特性

  • 强类型:编译时验证查询逻辑,减少运行时错误。
  • 延迟执行:LINQ查询通常是延迟执行的,即查询表达式本身不会立即执行,直到实际遍历结果时才触发查询。使用 ToList()ToArray()ToDictionary()FirstOrDefault()等方法可立即执行。
  • 支持多种数据源:LINQ可以用于查询多种数据源,如LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to Entities(Entity Framework)等。

LINQ中常用方法

操作示例数据

        public class StudentInfo
        {
            public int StudentID { get; set; }
            public string StudentName { get; set; }
            public DateTime Birthday { get; set; }
            public int ClassID { get; set; }
            public string Address { get; set; }
            public List<Course> Courses { get; set; } = new List<Course>();
        }

        public class Course
        {
            public int CourseID { get; set; }
            public string CourseName { get; set; }
        }

        static List<StudentInfo> students = new List<StudentInfo>
        {
            new StudentInfo
            {
                StudentID=1,
                StudentName="大姚",
                Birthday=Convert.ToDateTime("1997-10-25"),
                ClassID=101,
                Courses = new List<Course>
                {
                    new Course { CourseID = 101, CourseName = "语文" },
                    new Course { CourseID = 102, CourseName = "数学" }
                }
            },
            new StudentInfo
            {
                StudentID=2,
                StudentName="李四",
                Birthday=Convert.ToDateTime("1998-10-25"),
                ClassID=101,
                Courses = new List<Course>
                {
                    new Course { CourseID = 101, CourseName = "语文" },
                    new Course { CourseID = 102, CourseName = "数学" }
                }
            },
            new StudentInfo
            {
                StudentID=3,
                StudentName="王五",
                Birthday=Convert.ToDateTime("1999-10-25"),
                ClassID=102,
                Address="广州",
                Courses = new List<Course>
                {
                    new Course { CourseID = 101, CourseName = "语文" },
                    new Course { CourseID = 102, CourseName = "数学" }
                }
            },
            new StudentInfo
            {
                StudentID=4,
                StudentName="时光者",
                Birthday=Convert.ToDateTime("1999-11-25"),
                ClassID=102,
                Address="深圳" ,
                Courses = new List<Course>
                {
                    new Course { CourseID = 104, CourseName = "历史" },
                    new Course { CourseID = 103, CourseName = "地理" }
                }
            }
        };

基本查询方法

  • Where:用于过滤集合中的元素。
  • Select:用于将集合中的每个元素投影(转换)为新形式。
  • SelectMany:用于将多个集合展平为一个集合。
            var femaleStudents = students.Where(s => s.StudentName == "时光者");
            var studentNames = students.Select(s => s.StudentName);

            // 使用SelectMany展平所有学生的课程列表
            var allCourses = students.SelectMany(student => student.Courses).ToList();

            // 输出所有课程的名称
            foreach (var course in allCourses)
            {
                Console.WriteLine(course.CourseName);
            }

转换方法

  • ToList:将实现了IEnumerable<T>接口的集合转换为一个List<T>类型的对象,属于将集合转换为特定类型列表的方法。
  • ToArray:将一个实现了IEnumerable<T>接口的集合转换为一个数组,属于将集合转换为数组类型的方法。
  • ToDictionary:将一个集合转换为一个字典(Dictionary<TKey, TValue>),其中集合的元素作为字典的键或值(通过提供的键选择器和值选择器函数)。属于将集合转换为键值对集合(字典)的方法。
  • ToLookup:将一个序列分组并返回一个ILookup<TKey, TElement>对象,这是一个one-to-many集合,即一个键可以对应多个值。属于将集合转换为分组集合(查找表)的方法。
            var studentList = students.ToList();
            var studentArray = students.ToArray();
            var studentDictionary = students.ToDictionary(s => s.StudentID, s => s.StudentName);
            var studentLookup = students.ToLookup(s => s.ClassID, s => s.StudentName);

元素操作方法

  • First:返回集合中的第一个元素。
  • FirstOrDefault:返回集合中的第一个元素,如果集合为空则返回默认值。
  • Single:返回集合中的单个元素,如果集合为空或包含多个元素则抛出异常。
  • SingleOrDefault:返回集合中的单个元素,如果集合为空或包含多个元素则返回默认值。
  • Last:返回集合中的最后一个元素。
  • LastOrDefault:返回集合中的最后一个元素,如果集合为空则返回默认值。
  • ElementAt:返回集合中指定索引处的元素。
  • ElementAtOrDefault:返回集合中指定索引处的元素,如果索引超出范围则返回默认值。
  • DefaultIfEmpty:如果集合为空,则返回一个包含默认值的集合。
            var firstStudent = students.First();
            var firstAdult = students.FirstOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18));
            var onlyWangWu = students.Single(s => s.StudentName == "王五");
            var wangWuOrDefault = students.SingleOrDefault(s => s.StudentName == "王六");
            var lastStudent = students.Last();
            var lastAdult = students.LastOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18));
            var secondStudent = students.ElementAt(1);
            var tenthStudentOrDefault = students.ElementAtOrDefault(9);
            var nonEmptyStudents = students.DefaultIfEmpty(new StudentInfo { StudentID = 0, StudentName = "默认Student", Address = "默认" });

排序方法

  • OrderBy:用于对集合进行升序排序。
  • OrderByDescending:用于对集合进行降序排序。
  • ThenBy:用于在已排序的集合上应用次要排序。
  • ThenByDescending:用于在已排序的集合上应用次要降序排序。
            var sortedByBirthdayAsc = students.OrderBy(s => s.Birthday);
            var sortedByClassIDDesc = students.OrderByDescending(s => s.ClassID);
            var sortedByNameThenClassID = students.OrderBy(s => s.StudentName).ThenBy(s => s.ClassID);
            var sortedThenByDescending = students.OrderBy(s => s.StudentName).ThenBy(s => s.ClassID).ThenByDescending(x => x.Birthday);

聚合方法

  • Count:返回集合中的元素数量。
  • Sum:返回集合中数值类型元素的和。
  • Average:返回集合中数值类型元素的平均值。
  • Min:返回集合中的最小值。
  • Max:返回集合中的最大值。
  • Aggregate:对集合进行自定义聚合操作。
            int studentCount = students.Count();
            int totalClassID = students.Sum(s => s.ClassID);
            double averageAge = students.Average(s => DateTime.Now.Year - s.Birthday.Year);
            int minClassID = students.Min(s => s.ClassID);
            int maxClassID = students.Max(s => s.ClassID);
            string concatenatedNames = students.Aggregate("", (acc, s) => acc == "" ? s.StudentName : acc + ", " + s.StudentName);

集合操作方法

  • Distinct:返回集合中的唯一元素。
  • Union:返回两个集合的并集。
  • Intersect:返回两个集合的交集。
  • Except:返回在第一个集合中存在但不在第二个集合中存在的元素。
  • Concat:连接两个或多个集合,并返回一个新的序列。
            var uniqueClassIDs = students.Select(s => s.ClassID).Distinct();
            var unionClassIDs = uniqueClassIDs.Union(new[] { 103, 104 });
            var intersectClassIDs = uniqueClassIDs.Intersect(new[] { 101, 103 });
            var exceptClassIDs = uniqueClassIDs.Except(new[] { 101 });
            var concatClassIDs = uniqueClassIDs.Concat(new[] { 103, 104 });

分组与连接方法

  • GroupBy:根据键对集合进行多级分组。
  • Join:根据匹配键连接两个集合。
  var groupedByClassID = students.GroupBy(s => s.ClassID);
  var otherStudent = new List<StudentInfo>
  {
     new StudentInfo
     {
         StudentID=4,
         StudentName="摇一摇",
         Birthday=Convert.ToDateTime("2997-10-25"),
         ClassID=101,
         Courses = new List<Course>
         {
             new Course { CourseID = 101, CourseName = "语文" },
             new Course { CourseID = 102, CourseName = "数学" }
         }
     }
  };
  var listJoin = students.Join(
      otherStudent, // 要连接的第二个序列
      s1 => s1.StudentID, // 从第一个序列中提取键
      s2 => s2.StudentID, // 从第二个序列中提取键
      (s1, s2) => new // 结果选择器,指定如何从两个匹配元素创建结果
      {
          StudentID = s1.StudentID,
          StudentName = s1.StudentName,
          Birthday = s1.Birthday,
          ClassID = s1.ClassID,
          Address = s1.Address,
          Courses = s1.Courses,
          OtherStudentName = s2.StudentName //假设我们想要包含第二个序列中学生的名称
      });

跳过与获取指定数量的元素(常用作分页)

  • Skip:用于跳过集合中指定数量的元素,并返回剩余的元素序列。
  • Take:用于从集合的开头获取指定数量的元素,并返回一个新的序列。
            var skippedStudents = students.Skip(1);
            var takenStudents = students.Take(2);

            //数据分页查询(Skip + Take)
            int pageNumber = 2;
            int pageSize = 10;
            var pagedUsers = skippedStudents
                .OrderBy(u => u.ClassID) // 必须排序
                .Skip((pageNumber - 1) * pageSize)
                .Take(pageSize)
                .ToList();

条件判断方法

  • All:用于判断集合中的所有元素是否都满足指定条件。
  • Any:检查集合是否包含任何元素,或用于判断集合中是否存在至少一个满足指定条件的元素。
  • Contains:用于判断集合中是否包含指定的元素。
            bool allAdults = students.All(s => s.Birthday <= DateTime.Now.AddYears(-18));
            bool anyAdults = students.Any(s => s.Birthday <= DateTime.Now.AddYears(-18));
            bool containsWangWu = students.Contains(students.First(s => s.StudentName == "王五"));

更多方法查询

查询语法

LINQ提供了类似于SQL的查询语法,允许开发者以几乎相同的方式对不同类型的数据源进行查询。查询语法使用from、where、select、orderby等关键字。

            var querySyntaxResult = from student in students
                                    where student.ClassID == 101
                                    orderby student.StudentName ascending
                                    select student;

            Console.WriteLine("查询语法结果:");
            foreach (var student in querySyntaxResult)
            {
                Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
            }

查询关键字:

  • from: 指定数据源和范围变量(类似于迭代变量)。
  • where: 基于由逻辑 AND 和 OR 运算符(&& 或 ||)分隔的一个或多个布尔表达式筛选源元素。
  • select: 指定执行查询时,所返回序列中元素的类型和形状。
  • group: 根据指定的密钥值对查询结果分组。
  • into: 提供可作为对 join、group 或 select 子句结果引用的标识符(简单理解用于将配对的结果收集到一个临时序列)。
  • orderby: 根据元素类型的默认比较器对查询结果进行升序或降序排序。
  • join: 基于两个指定匹配条件间的相等比较而联接两个数据源(简单理解根据指定的键将两个序列中的元素配对)。
  • let: 引入范围变量,在查询表达式中存储子表达式结果。
  • in: join子句中的上下文关键字。
  • on: join子句中的上下文关键字。
  • equals: join子句中的上下文关键字。
  • by: group 子句中的上下文关键字。
  • ascending: orderby子句中的上下文关键字。
  • descending: orderby子句中的上下文关键字。

方法语法

方法语法也称为扩展方法语法,使用点号“.”和一系列扩展方法来构建查询。

            var methodSyntaxResult = students
                                    .Where(student => student.ClassID == 101)
                                    .OrderBy(student => student.StudentName)
                                    .ToList();

            Console.WriteLine("方法语法结果:");
            foreach (var student in methodSyntaxResult)
            {
                Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
            }

混合查询和方法语法

            var mixedResult = (from student in students
                               where student.ClassID == 101
                               where student.Courses.Any(course => course.CourseName == "数学")
                               orderby student.StudentName ascending
                               select student)
                       .Take(2)
                       .ToList();

            // 输出结果
            Console.WriteLine("混合查询结果:");
            foreach (var student in mixedResult)
            {
                Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
            }

参考文章

C# LINQ 快速入门实战指南,建议收藏学习!的更多相关文章

  1. logstash快速入门实战指南-Logstash简介

    作者其他ELK快速入门系列文章 Elasticsearch从入门到精通 Kibana从入门到精通 Logstash是一个具有实时流水线功能的开源数据收集引擎.Logstash可以动态统一来自不同来源的 ...

  2. webpack快速入门——实战技巧:watch的正确使用方法,webpack自动打包

    随着项目大了,后端与前端联调,我们不需要每一次都去打包,这样特别麻烦,我们希望的场景是,每次按保存键,webpack自动为我们打包,这个工具就是watch! 因为watch是webpack自带的插件, ...

  3. Linq快速入门——Lambda表达式的前世今生

    Linq快速入门——Lambda表达式的前世今生   Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托. 何为委托 ...

  4. webpack快速入门——实战技巧:开发和生产并行设置

    package.json中,devDependencies和dependencies是不同的 devDependencies:开发依赖 dependencies:生产依赖(线上) 1.安装生产环境的依 ...

  5. webpack快速入门——实战技巧:webpack模块化配置

    首先在根目录,新建一个webpack_config文件夹,然后新建entry_webpack.js文件,代码如下: const entry ={}; //声明entry变量 entry.path={ ...

  6. webpack快速入门——实战技巧:优雅打包第三方类库

    下面说两种方法: 一. 1.引入jQuery,首先安装: cnpm install --save jquery 2.安装好后,在我们的entry.js中引入: import $ from 'jquer ...

  7. webpack快速入门——实战技巧:webpack优化黑技能

    1.抽离jquery,vue(多个第三方类库抽离) 修改入口文件(webpack.config.js中) entry: { entry: './src/entry.js', jquery:'jquer ...

  8. 超长干货丨Kubernetes网络快速入门完全指南

    Kubernetes网络一直是一个非常复杂的主题.本文将介绍Kubernetes实际如何创建网络以及如何为Kubernetes集群设置网络. 本文不包括如何设置Kubernetes集群.这篇文章中的所 ...

  9. ❤️❤️新生代农民工爆肝8万字,整理Python编程从入门到实践(建议收藏)已码:8万字❤️❤️

    @ 目录 开发环境搭建 安装 Python 验证是否安装成功 安装Pycharm 配置pycharm 编码规范 基本语法规则 保留字 单行注释 多行注释 行与缩进 多行语句 数据类型 空行 等待用户输 ...

  10. Linq快速入门——扩展方法

    Linq为我们提供了许多扩展方法,方便我们对数据源进行操作(Where,Select...).即使你不了解算法,也能使用Linq当回牛人.扩展方法本质并不是什么高深的技术,说白了就是一个Static静 ...

随机推荐

  1. 第八章 (Nginx+Lua)流量复制/AB测试/协程

    流量复制 在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线.这其实就需要进 ...

  2. 一款基于 .NET8 + Vue 开源、免费、跨平台的企业级在线考试系统

    前言 今天大姚给大家分享一款基于 .NET8 + Vue 开源.免费(AGPL-3.0开源协议).跨平台的企业级在线考试系统:XBLMS. 项目介绍 XBLMS是一款基于 .NET8 + Vue 开源 ...

  3. Win7远程桌面连接不上

    Windows远程桌面连接不上有多种情况,当完成基本设置后,如果连不上,那么最可能的情况是防火墙和网络设置不匹配. 1. 检查一下Window防火墙中远程桌面的设置,默认情况下只允许"家庭/ ...

  4. flutter如何搭建android环境

    1.电脑上按安装sdk 首先配置Java的JDK 配好后,输入java 出现内容说明安装成功 然后在输入javac 出现内容说明jre安装成功 2.电脑上安装android Studio 安卓下载地址 ...

  5. 在flink消费一段时间kafka后,kafka-group的offset被重置了是怎么回事?

    一.背景 腾讯Flink使用 KafkaSource API创建source端,源码中默认开启了checkpoint的时候提交offset 到kafka-broker.读取kafka数据写入到iceb ...

  6. 单机麒麟kylin安装

    https://archive.apache.org/dist/kylin/ 2.5.0版本 首先启动hadoop.hive.hbase 并记得设置环境变量 #JDK export JAVA_HOME ...

  7. Luogu P5663 CSP-J2019 加工零件 题解 [ 绿 ] [ 分层图最短路 ]

    加工零件:非常好的一道图论题.CCF 普及组的题目大概也只有图论出的比较巧妙了. 题意简述:给你一张无向图,\(q\) 次询问,判断是否存在一条从 \(a\) 到 \(1\) 且长度为 \(L\) 的 ...

  8. 《<吕氏春秋> 刘本》

    <<吕氏春秋> 刘本>(卷三) 编者 刘昱合 其他版本 <吕氏春秋>(卷一) <吕氏春秋>(卷二) <<吕氏春秋> 全本>(卷三 ...

  9. 开源一款串口舵机驱动扩展板-FreakStudio多米诺系列

    原文链接: FreakStudio的博客 摘要 总线舵机扩展板通过UART接口控制多个舵机,支持堆叠级联,最多连接4个扩展板.具备小尺寸设计.供电保护.全双工转半双工通信.稳定供电等特点,适用于多舵机 ...

  10. 在鹅厂做java开发是什么体验

    离职已有好几个月,准备写一篇关于之前在腾讯做Java开发的经历,现在来谈谈在Java领域里,在腾讯做Java开发的体验.随便写写别较真. 首先,介绍一下腾讯里与Java相关的部门.主要有CDG(云与智 ...