语言集成查询LINQ

定义:语言集成查询 (LINQ) 是一系列直接将查询功能集成到 C# 语言的技术统称(用C#语言就能查询(数据库)等数据源的查询)

IEnumerable和List的区别:IEnumerable是一个接口,只能用来遍历里面的数据

ListList是一个类,它实现了IEnumerable接口,提供了丰富的成员方法,如Add、Remove、Clear、Sort等

LINQ表达式查询语法:以 from 子句开头,且必须以 select 或 group 子句结尾

csharp 复制代码
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 //查询变量
var evenNumbersSorted = from number in numbers
                        where number % 2 == 0
                        orderby number
                        select number;//select选择要返回的结果

方法语法:一般联合lamdba表达式一起使用:

csharp 复制代码
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 
var evenNumbersSorted = numbers.Where(number => number % 2 == 0)
                               .OrderBy(number => number)
                               .ToList();

查询语法和方法语法可以联合使用

.Max()、Count()为方法语法

csharp 复制代码
//不是查询变量的变量(因为储存了值)
int maxValue = (from number in numbers
                select number).Max();
int countAboveHalfMax = (from number in numbers
                         where number > maxValue / 2
                         select number).Count();

查询时不会立即执行,必须使用 foreach 来返回结果,但是也可以通过方法,强制执行

csharp 复制代码
List<int> numQuery2 = (from num in numbers
                       where (num % 2) == 0
                       select num).ToList();

查询结束语句:group/select

group 子句

group 子句返回一个 IGrouping<TKey,TElement> 对象序列,,可以按照每个字符串中的第一个字母对字符串序列进行分组。 在这种情况下,第一个字母就是键,类型为 char,要对每个组执行附加查询操作,可使用上下文关键字 into 指定一个临时标识符。并最终使用一个select 语句或另一个 group 子句结束该查询

csharp 复制代码
var studentQuery2 =
    from student in students
    group student by student.Last[0] into g
    orderby g.Key//对分组进行排序
    select g;

对分组后的结果查询:嵌套循环遍历:第一层遍历出每个键值的组,第二层遍历每个组中的元素

csharp 复制代码
foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
     Console.WriteLine(studentGroup.Key);
     foreach (var student in studentGroup)
     {
         Console.WriteLine("   {0}, {1}", student.Last, student.First);
     }
 }

分组键可以是任意类型:

比如按照数值范围分组:

csharp 复制代码
var studentQuery =
            from student in students
            let avg = (int)student.Scores.Average()
            group student by (avg / 10) into g  //按照每个学生的分/10取整分组,这样在0-9,10-19....按照每十个区间的方式分组
            orderby g.Key
            select g;

        // Execute the query.
        foreach (var studentGroup in studentQuery)
        {
            int temp = studentGroup.Key * 10;
            Console.WriteLine("Students with an average between {0} and {1}", temp, temp + 10);
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }

复合建分组:

csharp 复制代码
group person by new {name = person.surname, city = person.city};

直接返回分组后的结果

csharp 复制代码
List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];

IEnumerable<IGrouping<int, int>> query = from number in numbers
                                         group number by number % 2;
//方法语法
IEnumerable<IGrouping<int, int>> query = numbers
    .GroupBy(number => number % 2);

select 子句

可以选择返回查询出序列中的元素类型,

默认和数据源序列中元素类型一样,也可显示转换

csharp 复制代码
//默认
IEnumerable<Country> sortedQuery =
    from country in countries
    orderby country.Area
    select country;
csharp 复制代码
//转换(映射)
var queryNameAndPop =
    from country in countries
    select new
    {
        Name = country.Name,
        Pop = country.Population
    };

使用"into"延续

如果select或group后还要继续进行查询,那么就可以通过into重命名查询结果,然后继续筛选查询

csharp 复制代码
var percentileQuery =
    from country in countries
    let percentile = (int)country.Population / 10_000_000
    group country by percentile into countryGroup
    where countryGroup.Key >= 20//筛选出键值>=20的组
    orderby countryGroup.Key 
    select countryGroup;

联表查询join

join 子句可用于将来自不同源序列并且在对象模型中没有直接关系的元素相关联。 唯一的要求是每个源中的元素需要共享某个(主键、外键)可以进行比较以判断是否相等的值。join 子句的输出形式取决于执行的联接的具体类型

内部链接
csharp 复制代码
//查询语句
var innerJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID //两表中只有相等的才会连接上
    select new { ProductName = prod.Name, Category = category.Name }; 
//方法语句
var innerJoinQuery=category.join(products,
                               category=>category.ID,
                               prod=>prod.CategoryID, 
                               (category,prod)=>new
                               {
                               ProductName=prod.name,
                                Category = category.Name 
                                }
                                );

分组联接

含有 into 表达式的 join 子句

将每组链接的结果,单独作为一个对象,继续之后的操作

csharp 复制代码
var innerGroupJoinQuery2 =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from prod2 in prodGroup
    where prod2.UnitPrice > 2.50M
    select prod2;

//方法语句
var innerGroupJoinQuery2=categories.groupJoin(products,
category=>category.ID,
prod=>prod.CategoryID, 
(category,prod)=>prodGroup,
where(prod2=>prod2.UnitPrice > 2.50M),
select(prod2=>prod2)
)
 .SelectMany(group => group);//展开分组

复合键

csharp 复制代码
IEnumerable<string> query =
    from teacher in teachers
    join student in students on new
    {
        FirstName = teacher.First,
        LastName = teacher.Last
    } equals new
    {
        student.FirstName,
        student.LastName
    }
    select teacher.First + " " + teacher.Last;
//方法语法
IEnumerable<string> query =teachers.join(students,
				teacher=>new{FirstName = teacher.First,LastName = teacher.Last},
				student=>new{student.FirstName,student.LastName},
				(teacher,student)=> $"{teacher.First} {teacher.Last}"
				);

多个join

csharp 复制代码
var query = from student in students
    join department in departments on student.DepartmentID equals department.ID
    join teacher in teachers on department.TeacherID equals teacher.ID
    select new {
        StudentName = $"{student.FirstName} {student.LastName}",
        DepartmentName = department.Name,
        TeacherName = $"{teacher.First} {teacher.Last}"
    };
//方法语句
var query = students
    .Join(departments, student => student.DepartmentID, department => department.ID,
        (student, department) => new { student, department })
    .Join(teachers, commonDepartment => commonDepartment.department.TeacherID, teacher => teacher.ID,
        (commonDepartment, teacher) => new
        {
            StudentName = $"{commonDepartment.student.FirstName} {commonDepartment.student.LastName}",
            DepartmentName = commonDepartment.department.Name,
            TeacherName = $"{teacher.First} {teacher.Last}"
        });

执行左外部联接

使用 LINQ 通过对分组 join 的结果调用 DefaultIfEmpty 方法来执行左外部 join。

第一步内联:

Department 对象列表基于与学生的 DepartmentID 匹配的 Department 对象的 ID,内部联接到 Student 对象列表。

第二部:

然后,您使用另一个from子句来展开这个中间序列,同时利用DefaultIfEmpty()方法来确保即使没有找到匹配的部门,学生记录仍然会被包含在结果中。

csharp 复制代码
var query =
    from student in students
    join department in departments on student.DepartmentID equals department.ID into gj
    from subgroup in gj.DefaultIfEmpty()
    select new
    {
        student.FirstName,
        student.LastName,
        Department = subgroup?.Name ?? string.Empty
    };
cpp 复制代码
//方法语句
var query = students
    .GroupJoin(
        departments,
        student => student.DepartmentID,
        department => department.ID,
        (student, departmentList) => new { student, subgroup = departmentList })//subgroup包含了每次匹配成功后的右表记录
    .SelectMany(
        joinedSet => joinedSet.subgroup.DefaultIfEmpty(),//即使匹配到的右表是null,任然保存左表的那行记录
        (student, department) => new
        {
            student.student.FirstName,
            student.student.LastName,
            Department = department.Name
        });

let 子句

将表达式的结果保存到新的范围变量中

csharp 复制代码
string[] names = ["Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia"];
IEnumerable<string> queryFirstNames =
    from name in names
    let firstName = name.Split(' ')[0]
    select firstName;

foreach (var s in queryFirstNames)
{
    Console.Write(s + " ");
}

对分组操作执行子查询

csharp 复制代码
ar queryGroupMax =
    from student in students
    group student by student.Year into studentGroup
    select new
    {
        Level = studentGroup.Key,
        HighestScore = (//找出力每个组中,每个学生的平均成绩的最高分
            from student2 in studentGroup  
            select student2.Scores.Average()
        ).Max()
    };
//方法语句

```csharp
var queryGroupMax =
    students
        .GroupBy(student => student.Year)
        .Select(studentGroup => new
        {
            Level = studentGroup.Key,
            HighestScore = studentGroup.Max(student2 => student2.Scores.Average())
        });

``

在运行时动态指定谓词筛选器Contains方法

当筛选条件不单一,则使用Contains方法

csharp 复制代码
int[] ids = [111, 114, 112];

var queryNames =
    from student in students
    where ids.Contains(student.ID)//筛选出student.ID在ids集合中的学生
    select new
    {
        student.LastName,
        student.ID
    };

在查询表达式中处理 null 值

编码防御型:

csharp 复制代码
var query1 =
    from c in categories
    where c != null
    join p in products on c.ID equals p?.CategoryID
    select new
    {
        Category = c.Name,
        Name = p.Name
    };
相关推荐
pchmi1 小时前
C# OpenCV机器视觉:红外体温检测
人工智能·数码相机·opencv·计算机视觉·c#·机器视觉·opencvsharp
m0_748248022 小时前
【MySQL】C# 连接MySQL
数据库·mysql·c#
xcLeigh6 小时前
WPF实战案例 | C# WPF实现大学选课系统
开发语言·c#·wpf
one9966 小时前
.net 项目引用与 .NET Framework 项目引用之间的区别和相同
c#·.net·wpf
xcLeigh6 小时前
WPF基础 | WPF 布局系统深度剖析:从 Grid 到 StackPanel
c#·wpf
军训猫猫头16 小时前
52.this.DataContext = new UserViewModel(); C#例子 WPF例子
开发语言·c#·wpf
AI+程序员在路上20 小时前
C#调用c++dll的两种方法(静态方法和动态方法)
c++·microsoft·c#
数据的世界0121 小时前
C#中的语句
服务器·c#