C#学习——LINQ

学习资料来源于B站up主花千芳的视频:https://www.bilibili.com/video/BV1xJ411g7Hm

C#学习——LINQ

查询表达式

查询表达式必须以 from 字句开头,以 select 或 group 字句结束。第一个 from 子句和最后一个 select 子句或group 子句之间,可以包含一个或多个 where 子句、let 子句、join 子句、orderby 子句、group 子句,甚至还可以是 from 子句。它包括 8 个基本子句,具体说明如下所示:

  • from 子句:指定查询操作的数据源和范围变量
  • select 子句:指定查询结果的类型和表现形式
  • where 子句:指定筛选元素的逻辑条件
  • let 子句:引入用来临时保存查询表达式中的子表达式结果的范围变量
  • orderby 子句:对查询结果进行排序操作,包括升序和降序
  • group 子句:对查询结果进行分组
  • into 子句:提供一个临时标识符。join 子句、group 子句或select 子句可以通过该标识符引入查询操作中的中间结果
  • join 子句:连接多个用于查询操作的数据源

下面通过几个实例具体使用上述子句。

  1. 可以通过多个 from 子句,指定多个查询操作的数据源

    int[] arr01 = new int[] { 0, 2, 1, 9, 4, 5, 3, 7, 8, 6 };
    int[] arr02 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                var query2 = from n in arr01
                             from m in arr02
                             select n + m;
    
  2. let 子句创建一个范围变量来存储结果。如下使用 let 子句对 arr01 中的值进行模2运算,得到的值存储在 nm 中,然后对 nm 使用 where 子句进行筛选。在此操作中,n 和 nm 之间存在对应关系。

    int[] arr01 = new int[] { 0, 2, 1, 9, 4, 5, 3, 7, 8, 6 };
    var query = from n in arr01
        let nm = n % 2  //创建一个范围变量来存储结果
        where nm == 0
        select n;
    
  3. select 子句可以设置返回的查询结果的表现形式,如下,设置返回结果为 匿名对象。

    同时,下例中使用 orderby 子句对 查询结果进行逆序排序。

    var query3 = from n in arr01
        where n > 4
        orderby n descending
        select new  //返回一个匿名对象
    {
        Id = n,
        name = n.ToString()
    };
    
  4. 如下实例。groub…by…子句用于分组;into 子句将上半部分的查询数据临时存储到 g 中;后续的 from 子句就用 g 作为数据源进行查询。

    var query5 = from n in arr02
        where n > 4
        group n by n % 2
        into g   //临时标识符,即 将上面的查询数据临时存储到 g 中
        from ng in g
        select ng;
    
  5. join…on…子句进行连接查询。如下子句,使用 join 子句关联数据源 arr02 和 arr03;然后使用 on 进行设置筛选条件“ a equals b”。

    int[] arr02 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int[] arr03 = new int[] { 0, 2, 4, 6, 8 };
    var query6 = from a in arr02
        where a < 7
        join 
        b in arr03 
        on a equals b
        select a;
    //返回结果 0,2,4,6
    

LINQ 理论

LINQ,语言集成查询(LanguageIntegratedQuery)是一组用于 C# 和 Visual Basic 语言的扩展。它允许编写C#或者 Visual Basic 代码以查询数据库相同的方式操作内存数据。

语言集成查询(LINQ)是一组技术的名称,这些技术建立在将查询功能的接集成到C#语言(以及 Visual Basic 和可能的任何其他 .NET 语言)的基础上。借助于LINQ查询现在已是高级语言构造,就如同类、方法、事件等等。

对于编写查询的开发人员来说,LINQ 最明显的“语言集成“部分是查询表达式。查询表达式是使用 C#3.0 中引入的声明性查询语法编写的。通过使用查询语法,您甚至可以使用最少的代码对数据源执行复杂的筛选、排序和分组操作。您使用相同的基本查询表达式模式来查询和转换 SQL 数据库、ADO.NET 数据集、XML 文档和流以及 .NET 集合中的数据。LINQ 实现了统一的数据访问方案。

LINQ 构架:

在这里插入图片描述

LINQ 的查询运算符

在这里插入图片描述

查询表达式的关键字

在这里插入图片描述

LINQ 与 SQL 的区别与联系

  • LINQ 是面向对象的 SQL。也就是说,SQL 是向关系型数据库的查询,而 LINQ 实际上是对内存里
    的数据的查询。
  • LINQ 也可以通过表达式分析与实体到关系的映射(linq to sql),把 linq 转换为 sql 语句或是对 xml 的查询(linq to xml)。
  • 因此,LINQ 这种技术就成了对象到数据库记录的一个方便的映射、转化与操作的工其,你再也不必去根据不同的情况用字符串拼接的办法生成sql,而是专心于对象模型的处理即可,你对于对象的修改最终都会被转换为对应的 update、insert、delete 等 sql 语句,在你 submit 时全部提交到数据库中。概言之,linq to sql 是一个数据库到对象结构的一个中间层,它把对关系数据的管理转变为对象的操作,屏蔽了麻烦的 sql。

LINQ 分类

  • LINQ 包括五个部分:LINQ to Objects、LINQ to DataSets、LINQ to SQL、LINQ to Entities、LINQ to XML。
  • LINQ to Object:LINQ的核心组件,将查询表达式与 C# 编程语言集成,实现对内存对象表示的数据源
    进行查询和分析。数据源为实现了接口 IEnumerable<T> 或 IQueryable<T> 的内存数据集合。
  • LINQ to ADO.NET:将LINQ与ADO.NET集成,借助ADO.NET实现对多种数据库数据的查询和分析。包括
    LINQ to SQL和 LINQ to DataSet,前者主要操作外部数据库,后者操作内存DataSet。
  • LINQ to XML:是一种全新的XML数据操作模式,数据源为XML文档,通过XElement、Xattribute等类型
    将XML文档数据加载到内存,通过LINQ进行数据查询。
  • LINQ to Entities:使开发人员能够通过使用LINQ表达式和LINQ标准查询运算符,直接从开发坏境中
    针对实体框架对象上下文创建灵活的强类型查询。LINQ to Entities查询使用对象服务基础结构。

LINQ 查询的四个要素

LINQ查询的根本目的时从指定的数据源查询满足符合特定条件的元素,并且根据需要对这些查询到的元素进行排序、连接等操作。

LINQ查询包括四个主要元素:

  1. 数据源。数据源表示LINQ查询将从哪里查找数据,它通常是一个或多个数据集,每个数据集包含一系列的元素。
  2. 目标数据。目标数据用来指定查询具体想要的是什么数据,在LNQ中,它定义了查询结果数据集中元素的具体类型。
  3. 筛选条件。筛选条件定义了对数据源中元素的过滤条件,只有满足条件的元素才作为查询结果返回。筛选条件可以是简单的逻辑表达式,也可以是复杂的逻辑函数。
  4. 附件操作。附加操作表示一些其他的对查询结果的辅助操作。比如,对查询结果进行排序、分组等。

数据源和目标数据是LINQ查询的必备元素,筛选条件和附加操作是可选。

【注意】:LINQ查询代码中关键字必须小写。

LINQ 初级实例

LINQ 的初级实例,对字符串进行操作。如下代码,其中:languageGroup.Key 即为 item.Length,表示分组条件(字符串的长度)。这里涉及“协变与逆变”的内容,如下文补充。

string[] languages = new string[] { "c", "c++", "c#", "java", "Go", "python", "rust" };
var query7 = from item in languages
    group item by item.Length into languageGroup
    orderby languageGroup.Key
    select languageGroup;

协变与逆变

“协变” 是指能够使用与原始指定的派生类型相比,派生程度更大的类型。
“逆变“ 则是指能够使用派生程度更小的类型

“协变“ -> “和谐的变“ -> “很自然的变化“ -> string->object:协变。
“逆变“ -> “逆常的变“ -> “不正常的变化“ -> object->string:逆变。

对此,C# 提供以下两个关键字

  • out:输出结果。协变。
    • 如果一个泛型参数标记为 out,代表它是用来输出的,作为结果返回。
  • in:作为参数。逆变。
    • 如果一个泛型参数标记为 in,代表它是用来输入的,只能作为参数被使用,不能被返回。

如下代码,代码中 Dog 类继承自 Animal 类:

List<Dog> dogs = new List<Dog>();
//List<Animal> animals = dogs;  //此处不可进行赋值。
//需要做如下处理才可以赋值
List<Animal> animals = dogs.Select(d => (Animal)d).ToList();

IEnumerable<Dog> someDogs = new List<Dog>();
IEnumerable<Animal> someAnimals = someDogs;  //此次可以进行赋值,因为 IEnumerable 的泛型定义为 IEnumerable<out T>,强制转化为合法——协变

Action<Animal> actionAnimal = new Action<Animal>(a => { /*具体方法*/});
Action<Dog> actionDog = actionAnimal;  //此处为逆变
actionDog(new Dog());

LINQ 综合实例

  1. 查询表达式 与 扩展方法Where+Lambda。

    其中,Where 方法与 查询表达式的 where 子句等价。

    以下两个查询语句结果相同。

    var query = from e in employees
        where e.Level > 8 && e.Department == "AL"
        select e;
    var query2 = employees.Where(m => e.Level > 8 && e.Department == "AL");
    
  2. 扩展方法 SelectMany() 与查询表达式中的多个 from 等价。

    var masterKongfu = from m in master
        from k in kongfu
        where k.Lethality > 90 && m.Kongfu == k.KongfuName
        orderby m.Level
        select m.Id + " " + m.Name + " " + m.Age + " " + m.Menpai + " " + m.Kongfu + " " + m.Level + " ";
    
    var masterKongfuMethod = master.SelectMany(k => kongfu, (m, k) => new { mt = m, kf = k })
        .Where(x => x.kf.Lethality && m.mt.Kongfu == x.kf.KongfuName)
        .OrderBy(m => m.mt.Level)
        .Select(m => m.mt.Id + " " + m.mt.Name + " " + m.mt.Age + " " + m.mt.Menpai + " " + m.mt.Kongfu + " " + m.mt.Level + " ")
    

    对于上文中排序,如果是逆序的,且存在多个排序条件,可以做如下修改:

    var masterKongfuMethod = master.SelectMany(k => kongfu, (m, k) => new { mt = m, kf = k })
        .Where(x => x.kf.Lethality && m.mt.Kongfu == x.kf.KongfuName)
        .OrderByDescending(m => m.mt.Level)
        .ThenBy(m => m.mt.Age)
        .ThenBy(m => m.mt.Name)
        .Select(m => m.mt.Id + " " + m.mt.Name + " " + m.mt.Age + " " + m.mt.Menpai + " " + m.mt.Kongfu + " " + m.mt.Level + " ")
    
  3. 查询表达式和扩展方法中 join 的使用;查询结果返回新的匿名对象
    在这里插入图片描述

  4. 使用 GroupJoin 方法进行分组
    在这里插入图片描述

  5. Join 和 GroupJoin 的区别
    在这里插入图片描述

  6. 扩展方法 GroupBy() 的使用
    在这里插入图片描述

  7. 量词操作:Any、All、Contains 等
    在这里插入图片描述
    在这里插入图片描述

  8. 分页操作

  9. Skip:跳过序列中指定的数量的元素,然后返回剩余的值

  10. Take:做序列的开头,返回指定序列的元素
    在这里插入图片描述
    在这里插入图片描述

  11. 集合操作符:

  12. Intersecrt:返回交集,即返回两个序列中都又的元素。

  13. Union:并集,且没有重复元素

  14. Concact:返回两个并集

  15. Except:差集,返回只出现在一个序列中的元素
    在这里插入图片描述

  16. 合计操作符 Aggregate。如下代码中:

- x = 6
- y = 6
- x = 12。因为 z 中 ” r = r * 2“ 表示在累加的结果上,再进行 * 2

在这里插入图片描述

  1. DefaultIfEmpty 实现左外部联接。
    左外部联接是这样定义的:返回第一个集合的每个元素,无论该元素在第二个集合中是否有任何相关元素。 可以使用 LINQ 通过对分组联接的结果调用 DefaultIfEmpty 方法来执行左外部联接。
var query = from person in people
                join pet in pets on person equals pet.Owner into gj
                from subpet in gj.DefaultIfEmpty()
                select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
  1. 使用组合键进行联接。
    想要使用多个键来定义匹配的联接操作。 使用组合键来完成此操作。 以匿名类型或包含要比较值的命名类型的形式来创建组合键。
var query = from o in db.Orders
   from p in db.Products
   join d in db.OrderDetails
       on new {o.OrderID, p.ProductID} equals new {d.OrderID, d.ProductID} into details
       from d in details
       select new {o.OrderID, p.ProductID, d.UnitPrice};
  1. 在查询表达式中处理 null 值。
    可采用防御方式进行编码,以避免空引用异常,如以下示例所示。此示例显示如何在源集合中处理可能的 null 值。 IEnumerable 等对象集合可包含值为 null 的元素。 如果源集合为 null 或包含值为 null 的元素,并且查询不处理 null 值,则在执行查询时将引发 NullReferenceException。
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 };

在 join 子句中,如果只有一个比较键是可以为 null 的值类型,则可以在查询表达式中将另一个比较键转换为可以为 null 的值类型。 在以下示例中,假定 EmployeeID 是包含 int? 类型的值列:

void TestMethod(Northwind db)
{
    var query =
        from o in db.Orders
        join e in db.Employees
            on o.EmployeeID equals (int?)e.EmployeeID
        select new { o.OrderID, e.FirstName };
}
  1. 在查询表达式中处理异常。
    在某些情况下,针对由查询内部引发的异常的最佳措施可能是立即停止执行查询。 下面的示例演示如何处理可能在查询正文内部引发的异常。 假定 SomeMethodThatMightThrow 可能导致要求停止执行查询的异常。
class QueryThatThrows
{
    static void Main()
    {
        // Data source.
        string[] files = { "fileA.txt", "fileB.txt", "fileC.txt" };

        // Demonstration query that throws.
        var exceptionDemoQuery =
            from file in files
            let n = SomeMethodThatMightThrow(file)
            select n;

        // Runtime exceptions are thrown when query is executed.
        // Therefore they must be handled in the foreach loop.
        try
        {
            foreach (var item in exceptionDemoQuery)
            {
                Console.WriteLine($"Processing {item}");
            }
        }

        // Catch whatever exception you expect to raise
        // and/or do any necessary cleanup in a finally block
        catch (InvalidOperationException e)
        {
            Console.WriteLine(e.Message);
        }

        //Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }

    // Not very useful as a general purpose method.
    static string SomeMethodThatMightThrow(string s)
    {
        if (s[4] == 'C')
            throw new InvalidOperationException();
        return @"C:\newFolder\" + s;
    }
}
/* Output:
    Processing C:\newFolder\fileA.txt
    Processing C:\newFolder\fileB.txt
    Operation is not valid due to the current state of the object.
 */
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值