首页 > 编程笔记 > C#笔记 阅读:38

C# LINQ查询详解(附带实例)

LINQ 全名为 Language Integrated Query,是 Microsoft 公司开发的语言集成查询,目前这项技术已经整合至 C# 语言。

过去我们学习数据库,如 SQL、XML文件、各种 Web 服务等时,必须针对每一种数据库学习查询语言,现在 Microsoft 公司将查询功能整合,设计了 LINQ,在这个架构下有相同的类、方法和事件。

因此可以使用 LINQ 技术同时应用在下列领域:
对于撰写数据库查询语言的程序设计师而言,LINQ 最明显的特色是使用声明式查询语法,通过使用查询语法,可以用最少的程序代码对数据来源进行排序、筛选或分组。当然其最大的特色是,相同的查询运算语法可以同时应用在 SQL 数据库、ADO.NET 数据集、XML 文件以及 .NET 集合中。

LINQ 已经是不同程序语言的共享技术,可以作为不同程序语言和不同数据库之间的桥梁,如下图所示。

C# LINQ语法

LINQ 有 8 个基本表达式,可以参考下表:

表达式 说明
from 指定查询操作的数据来源,和变量的范围
select 筛选查询结果的类型和形式
where 筛选的逻辑条件
let 在数据查询过程中,我们可以使用 let 关键词创建和存储子表达式的结果,未来可以使用子表达式的结果作为查询的依据
orderby 针对查询结果排序操作
group ... by 对查询结果分组,每个分组是一个数组,然后可以用 foreach 输出分组元素
into 创建临时标识符,用于存储 group、join、select 子句的执行结果
join 依据键值连接多个序列,返回查询的结果

1) from/select/where实例

使用 LINQ 习惯上会使用 var 来声明隐型变量,这可以简化设计,然后变量真正的数据类型由语句右边的类型决定,也就是在程序编译时决定数据类型。右边推断的类型可能是 C# 内建类型、匿名类型、使用者自定义的类型,或是 .NET 类库中定义的类型。

【实例】 from 和 select 的基础应用,这个程序筛选出所有 arr 数组的内容,读者可以注意第 3 行,其使用 var 来声明查询结果变量 query。
int[] arr = new int[] { 1, 3, 5, 11, 21, 12, 6, 9, 20 };
var query = from n in arr select n;
foreach (var q in query)
    Console.Write($"{q}, ");
执行结果为:

1, 3, 5, 11, 21, 12, 6, 9, 20,


where 可以搭配关系表达式来筛选数据。例如,扩充实例 1,增加 where,筛选小于 10 的数组内容。
int[] arr = new int[] { 1, 3, 5, 11, 21, 12, 6, 9, 20 };
var query = from n in arr
            where n < 10 // 筛选 n < 10
            select n;
foreach (var q in query)
    Console.Write($"{q},");
执行结果为:

1,3,5,6,9,


假设现在要求读者筛选大于 5 且小于 15 的数据,这时需要使用 C# 的逻辑运算符“&&”,这将是读者的习题。where 可以搭配 bool 的条件式来筛选数据。

【实例】筛选偶数数据。
int[] arr = new int[] { 1, 3, 5, 11, 21, 12, 6, 9, 20 };
var query = from n in arr
            where (n % 2 == 0) // 筛选偶数
            select n;
foreach (var q in query)
    Console.Write($"{q},");
执行结果为:

12,6,20,


我们也可以将类数据用于筛选数据。例如,筛选主修是 CS(Computer Science) 的学生:
var students = new List<Student>() {
    new Student(){Id = 1, Name = "Mary", Major = "cs"},
    new Student(){Id = 2, Name = "Tom", Major = "EE"},
    new Student(){Id = 3, Name = "Sam", Major = "cs"},
    new Student(){Id = 4, Name = "John", Major = "cs"}
};

// 查询所有主修是CS的学生
var query = from s in students
            where s.Major == "cs"
            select s;

// 输出结果
foreach (var q in query)
    Console.WriteLine($"{q.Id}, {q.Name}");

public class Student {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Major { get; set; }
}
执行结果为:

1, Mary
3, Sam
4, John

2) let实例

【实例】let 关键词的应用,这个程序会输出开头字母是 c~k 的英文单词。
var keywords = new[] {
    "bool", "break", "byte",
    "catch", "char", "delegate", "do", "double",
    "else", "enum", "event", "explicit", "extern",
    "false", "finally", "float", "for", "foreach",
    "ref", "return", "short", "sizeof", "switch",
    "this", "throw", "true", "try", "typeof",
    "uint", "ulong", "ushort", "using",
    "virtual", "void", "volatile", "while"
};

var query = from words in keywords
            let firstLetter = words[0] // 取第1个字母
            where firstLetter >= 'c' && firstLetter <= 'd'
            select words;
foreach (var q in query)
    Console.Write($"{q}, ");
执行结果为:

catch, char, delegate, do, double,


【实例】将句子拆解为单词,然后输出以 a、i、t 开头的单词。
string[] news = {
    "Apple introduced Safety Check.",
    "This robust security setting.",
    "You may stop sharing your information."
};

// 将句子拆成单字数组,选择 'a'、'i'、't' 开头的单词
var query = from sentence in news
            let words = sentence.Split(' ') // 拆解句子成单词数组
            from word in words
            let w = word.ToLower() // 单词转成全部小写
            where w[0] == 'a' || w[0] == 'i' || w[0] == 't'
            select word;

// 输出结果
foreach (var v in query)
    Console.WriteLine($"{v} 是a、i、t开头单词");
执行结果为:

       Apple 是a、i、t开头单词
  introduced 是a、i、t开头单词
        This 是a、i、t开头单词
information. 是a、i、t开头单词

3) orderby实例

使用 orderby 可以执行排序操作,默认是从小到大排序,使用关键词可以执行从大到小排序。

【实例】 orderby 关键词的应用,第 3~8 行是执行从小到大排序,第 11~14 行是执行从大到小排序。
int[] arr = new int[] { 8, 3, 5, 11, 21, 12, 6, 9, 20 };
var query1 = from n in arr
             where n < 10 // 筛选 n < 10
             orderby n    // 默认从小到大排序
             select n;
foreach (var q in query1)
    Console.Write($"{q}, ");

Console.WriteLine(); // 跳行输出

var query2 = from n in arr
             where n < 10 // 筛选 n < 10
             orderby n descending // 从大到小排序
             select n;
foreach (var q in query2)
    Console.Write($"{q}, ");
执行结果为:

3, 5, 6, 8, 9,
9, 8, 6, 5, 3,

上述第 13 行也可以改为 orderby descending。

4) group by实例

分组是 LINQ 强大的功能之一,可以执行下列分组策略:
【实例】将 10 以下数字分奇数和偶数。
int[] arr = new int[] { 8, 3, 5, 11, 21, 12, 6, 9, 20 };
var query = from n in arr
            where n < 10 // 筛选 n < 10
            group n by n % 2; // 分组奇数和偶数
foreach (var element in query) // 元素 element 是分组数组
{
    foreach (var e in element) // 列出分组数组内容
        Console.WriteLine(e);
    Console.WriteLine("=====group组别分隔线=====");
}
执行结果为:

8
6
=====group组别分隔线=====
3
5
9
=====group组别分隔线=====

5) group by/into实例

当执行分组后,分组的键值可以用 Key 属性取得。例如,将 Student 类的数据依据科系主修分组。
var students = new List<Student>() {
    new Student(){Id = 1, Name = "Mary", Major = "cs"},
    new Student(){Id = 2, Name = "Tom", Major = "EE"},
    new Student(){Id = 3, Name = "Sam", Major = "cs"},
    new Student(){Id = 4, Name = "John", Major = "cs"},
    new Student(){Id = 5, Name = "Kevin", Major = "EE"},
    new Student(){Id = 6, Name = "Linda", Major = "cs"}
};

// 依科系分组
var query = from student in students
            group student by student.Major into newGroup
            select newGroup;

// 输出结果
foreach (var qGroup in query) {
    Console.WriteLine($"科系:{qGroup.Key}"); // Key是分组属性
    foreach (var student in qGroup) // 遍历分组
        Console.WriteLine($"\t{student.Id}, {student.Name}");
}

public class Student {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Major { get; set; }
}
执行结果为:

科系:cs
    1, Mary
    3, Sam
    4, John
    6, Linda
科系:EE
    2, Tom
    5, Kevin

6) join实例

关键词 join 用于连接数据,基本上需要外部序列 (outer sequence)、内部序列 (inner sequence)、键值 (key selector) 和结果选择,整个语法如下:
from … in 外部序列
join … in 内部序列
on 外部键值 equals 内部键值
select

【实例】 join 方法的应用,这个程序会查询相同元素的内容。
int[] outer = new int[] { 1, 2, 3, 4, 5, 6, 7 };
int[] inner = new int[] { 1, 3, 4, 7 };
var query = from a in outer // 取得outer
            join b in inner // 连接inner
            on a equals b // 筛选元素 a = b
            select b; // 筛选结果
foreach (var item in query)
    Console.WriteLine(item);
执行结果为:

1
3
4
7


【实例】 join 方法的应用,这个程序会列出 a 小于 5,b 大于 1,同时 a 等于 b 的结果。
int[] outer = new int[] { 1, 2, 3, 4, 5, 6, 7 };
int[] inner = new int[] { 1, 3, 4, 7 };
var query = from a in outer // 取得outer
            where a < 5 // 设定 a < 5
            join b in inner // 连接inner
            on a equals b // 筛选元素 a = b
            where b > 1 // 设定 b > 1
            select b; // 筛选结果
foreach (var item in query)
    Console.WriteLine(item);
执行结果为:

3
4


【实例】 join 方法的应用,这个程序会进行姓名 (Name) 和学院 (College) 的配对。
IList<Student> studentList = new List<Student>() {
    new Student(){ID = 1, Name = "Tom", Major = "CS"},
    new Student(){ID = 2, Name = "Kevin", Major = "ME"},
    new Student(){ID = 3, Name = "Bill", Major = "History"},
    new Student(){ID = 4, Name = "Jonny", Major = "Language"},
    new Student(){ID = 5, Name = "Mike", Major = "cs"}
};

IList<School> schoolList = new List<School>() {
    new School(){Major = "cs", College = "abc"},
    new School(){Major = "ME", College = "abc"},
    new School(){Major = "MBA", College = "Business"}
};

var query = from s in studentList // outer
            join st in schoolList // inner
            on s.Major equals st.Major // 筛选科系相同
            select new {
                name = s.Name, // 姓名
                major = s.Major, // 主修
                college = st.College // 学院
            };

foreach (var q in query)
    Console.WriteLine($"{q.name} 就读 {q.college} 学院主修是 {q.major}");

public class Student {
    public int ID { get; set; }
    public string Name { get; set; }
    public string Major { get; set; }
}

public class School {
    public string Major { get; set; }
    public string College { get; set; }
}
执行结果为:

Tom 就读 abc 学院主修是 CS
Kevin 就读 abc 学院主修是 ME
Mike 就读 abc 学院主修是 cs

相关文章