C# 基础知识系列- 8 Linq最后一部分查询表达式语法实践

1 前言

之前的几篇文章介绍了Lambda和Linq的一些支持方法。这一篇我尝试通过模拟具体的业务场景来描述一下Linq的两种查询方式的使用。

一直提的Linq查询方式分为两种,一种就是方法链的形式,官方的称呼是流式查询;另一种是类似于SQL语句的查询方式,我之前叫做类SQL查询方式,不过有的文档称之为查询表达式。
注意,本篇内容需要先看过 《C# 基础系列-7》,并有一定的对象和集合的基础。

1.1 数据准备:

因为这篇内容会涉及到多个数据源,所以这里需要准备一些类和数据,以下数据纯属虚构,不涉及到现实。

/// <summary>/// 学生/// </summary>public class Student{    /// <summary>    /// 学号    /// </summary>    public long StudentId { get; set; }    /// <summary>    /// 姓名    /// </summary>    public string Name { get; set; }    /// <summary>    /// 年龄    /// </summary>    public int Age { get; set; }    /// <summary>    /// 班级    /// </summary>    public string Class { get; set; }}/// <summary>/// 科目/// </summary>public class Subject{    /// <summary>    ///     /// </summary>    public long SubjectId { get; set; }    /// <summary>    /// 名称    /// </summary>    public string Name { get; set; }    /// <summary>    /// 年级    /// </summary>    public string Grade { get; set; }    /// <summary>    /// 教师    /// </summary>    public string Teacher { get; set; }}/// <summary>/// 考试/// </summary>public class Exam{    /// <summary>    /// 考试编号    /// </summary>    public long ExamId { get; set; }    /// <summary>    /// 科目编号    /// </summary>    public long SubjectId { get; set; }    /// <summary>    /// 学生编号    /// </summary>    public long StudentId { get; set; }    /// <summary>    /// 分数    /// </summary>    public double Score { get; set; }    /// <summary>    /// 考试时间:年-月 如202004 表示2020年04月    /// </summary>    public int Time { get; set; }}

数据源:

List<Student> students = new List<Student>();// 学生列表,忽略数据来源List<Subject> subjects = new List<Subject>();// 科目列表,忽略数据来源List<Exam> exams = new List<Exam>();// 考试列表,忽略数据来源

2 查询演示

预先介绍一个概念,C#中有一种类型叫做匿名类型。因为C#的要求是万物皆对象,对象皆有类,所以每一个对象或者数据都是有类型在背后支撑的。但是有时候会需要一些一次性的只读类型,这时候声明一个完整的类就有点得不偿失了。什么是一次性的只读类型呢,就是我们只关心它有哪些属性,不关心它有什么方法,同时这个类对应的对象只能在初始化的时候给属性赋值其他时候不能重新赋值,而且这个类型只在方法内部使用,在这个变量使用完成之后这个类型也失去了意义,这种类型就是我们所说的一次性的只读类型。

那么这种类型怎么声明呢?先不急,先再介绍一个关键字var。这个关键字有什么特别的地点吗?var 表示隐式“类型”,意思就是用var声明的变量其类型需要编译器自己结合上下文推断,也就是说使用者和声明者都知道这个变量的类型,但是没有明说。

那么为什么需要介绍var呢?原因在于,var 是匿名对象的基础。因为匿名对象不能用object声明变量,原因有两点,第一,变量声明为object之后,我们所需要的属性就无法使用了;第二,匿名类型的对象无法直接类型转换为object。所以,想要正常使用匿名类型,必须用var

下面简单演示一下匿名类型的声明和使用:

var obj = new {    Name = "萧三",    Age = 20};// obj.Name 萧三// obj.Age 20

这就是匿名类型,声明了一个有Name和Age属性的对象,这个对象我们知道它有哪些属性,但是不知道它的类型是什么。

在介绍完需要的知识后,将通过实际的情况来比较一下流式查询和查询表达式两种写法。

2.1 简单查询

  1. 查询班级是三年一班的所有同学

    // 流式查询var results = students.Where(t=>t.Class=="三年一班");// 查询表达式var results = from s in students    where s.Class == "三年一班"    select s;

    这两种查询方式结构都是IEnumerable<T>。

  2. 获取姓张的所有学生的花名册

    // 流式查询var results = students.Where(t=>t.Name.StartWith("张"));// 查询表达式var results = from s in students where s.Name.StartWith("张") select s;
  3. 按班级进行分组获取每个班的学生花名册

    // 流式查询var results = students.GroupBy(t => t.Class);// 查询表达式var results = from s in students group s by s.Class;// 注明:完整的写法如下:// var results = from s in students group s by s.Class into g select g;

    需要注意的是,如果返回结果是一个分组的结果,那么就不用select了。

  4. 查询每个班的平均年龄

    // 流式查询var results = students.GroupBy(t => t.Class)                .Select(t => new {Class = t.Key, AveAge = t.Average(p => p.Age)});// 查询表达式var results = from s in students                group s by s.Class                into g                select new {Class = g.Key, AveAge = g.Average(t => t.Age)};

    查询表达式中没有统计查询的相关关键字,只能通过方法来获取,同时查询表达式返回的是一个集合,所以没法直接通过查询表达式进行求和、求平均等。

  5. 对所有学生按照年龄大小从大到小进行排序

    // 流式查询var results = students.OrderByDescending(t => t.Age);// 查询表达式var results = from s in students orderby s.Age descending select s;
  6. 对所有学生按照年龄大小从小到大进行排序

    // 流式查询var results = students.OrderBy(t => t.Age);// 查询表达式var results = from s in students orderby s.Age select s;
  7. 先按年龄排序再按姓名进行排序

    // 流式查询var results = students.OrderBy(t => t.Age).ThenBy(t=>t.Name);//ThenByDescending 是降序版的ThenBy// 查询表达式var results = from s in students orderby s.Age //descending 如果是降序则增加这个关键字    , s.Name select s;

2.2 复杂查询

前一部分介绍了简单的查询,这一部分介绍联合多个数据源进行一系列的查询操作。

  1. 查询三年级语文科目在202004月举行的考试的所有成绩

    // 流式查询var results = subjects.Join(exams, s => s.SubjectId, e => e.StudentId, (s, e) => new{    s.Name,    s.Grade,    e.Score,    e.Time}).Where(t=>t.Grade == "三年级" && t.Name =="语文" && t.Time == 202004).Select(t=>t.Score);// 查询表达式var results = from s in subjects                join e in exams on s.SubjectId equals e.SubjectId                where e.Time == 202004 && s.Grade == "三年级" && s.Name == "语文"                select e.Score;
  2. 按年级进行分组,查询各年级语文分数

    // 流式查询var results = subjects.Where(p => p.Name == "语文")                .Join(exams, s => s.SubjectId, e => e.SubjectId, (s, e) => new {s.Grade, e.Score})                .GroupBy(t => t.Grade);// 查询表达式var results = from s in subjects                join e in exams on s.SubjectId equals e.SubjectId                where s.Name == "语文"                group e.Score by s.Grade                into g                select g;
  3. 求各年级历次考试各科目分数的平均分和最高分以及最低分

    //流式查询var results = subjects.Join(exams, s => s.SubjectId, e => e.SubjectId, (s, e) => new            {                s.Grade,                s.Name,                e.Score            }).GroupBy(t => t.Grade).Select(t => new            {                Grade = t.Key,                Subjects = t.GroupBy(p => p.Name).Select(p => new                {                    Name = p.Key,                    Max = p.Max(r => r.Score),                    Min = p.Min(r => r.Score),                    Average = p.Average(r => r.Score)                })            });//查询表达式var results = from s in subjects                join e in exams on s.SubjectId equals e.SubjectId                let o = new {s.Grade, s.Name, e.Score}                group o.Score by new {o.Grade, o.Name}                into o                let p = new                {                    o.Key.Grade, Subject = new {o.Key.Name,                                                 Max = o.Max(),                                                Min = o.Min(),                                                 Average = o.Average()}                }                group p.Subject by p.Grade                into g                                select new                {                    Grade = g.Key,                    Subjects = g.AsEnumerable()                };

    以上大概介绍了一下Linq的使用,明显可以看得出,流式查询和查询表达式在可读性上区别还是蛮大的。对于熟悉SQL的人,查询表达式能更快的上手;对于我来说,更习惯于用流式查询,不过在多数据源联合的时候,我更倾向于写查询表达式。以上是基础篇Linq的全部内容。

(0)

相关推荐

  • C# 基础知识系列-7 Linq详解

    前言 在上一篇中简单介绍了Linq的入门级用法,这一篇尝试讲解一些更加深入的使用方法,与前一篇的结构不一样的地方是,这一篇我会先介绍Linq里的支持方法,然后以实际需求为引导,分别以方法链的形式和类S ...

  • K线密码之裸K线入门基础知识系列教程(一)

    2019-06-26拾荒网 编辑:K线炮手 在股票市场如果不能坚持学习 那你是不可能有进步的,想进步就要不断学习股票各种技巧,其中k线的形态就能完美的体现出主力的意图 只要紧跟主力庄家我们才能收获多多 ...

  • K线密码之裸K线入门基础知识系列教程(二)

    K线密码之裸K线入门基础知识系列教程(二)

  • K线密码之裸K线入门基础知识系列教程(三)

    31.看涨吞没形态 应用法则: 1.看涨吞没形态出现在一轮明显的下跌趋势中,如果吞没形态具有下面列出了这样的一些参考性要素和特征,那么它们构成重要反转信号的可能性将大大地增强; 2.在看涨吞没形态中, ...

  • K线密码之裸K线入门基础知识系列教程(四)

    51.思量红三兵 应用法则: 1.虽然思量红三兵形态在一般情况下不属于顶部反转形态,但是有时候,它也能引出不容忽视的下跌行情.特别是若思量红三兵形态出现在一段上升行情的后期,当紧接着出现一个巨大的阴线 ...

  • 葡萄病害综合管理基础知识系列:来自链霉菌家族的农用抗生素

    在医学上,青霉素的发现与应用,可谓是人类健康史上的一次革命.但青霉素对结核杆菌的效果并不好.链霉素,发现于1943年10月19日,是美国罗格斯大学一名叫Albert Schatz 的博士生在著名微生物 ...

  • C# 基础知识系列- 1 数据类型

    常见数据类型 C#的类型一般分为值类型.引用类型两大类型. 值类型的实例存放在栈中,引用类型会在栈中放置一个指针指向堆中的某一块内容. C#为我们内置了几个数据类型供我们使用: 关键词简写 对应的类全 ...

  • C# 基础知识系列- 9 字符串的更多用法(一)

    0. 前言 在前面的文章里简单介绍了一下字符串的相关内容,并没有涉及到更多的相关内容,这一篇将尝试讲解一下在实际开发工作中会遇到的字符串的很多操作. 1. 创建一个字符串 这部分介绍一下如何创建一个字 ...

  • C# 基础知识系列- 12 任务和多线程

    0. 前言 照例一份前言,在介绍任务和多线程之前,先介绍一下异步和同步的概念.我们之间介绍的知识点都是在同步执行,所谓的同步就是一行代码一行代码的执行,就像是我们日常乘坐地铁通过安检通道一样,想象我们 ...