• 快读《ASP.NET Core技术内幕与项目实战》EFCore2.5:集合查询原理揭秘IQueryable和IEnumerable


    本节内容,涉及4.6(P116-P130)。主要NuGet包:如前述章节

     

    一、LINQ和EFCore的集合查询扩展方法的区别

    1、LINQ和EFCore中的集合查询扩展方法,虽然命名和使用完全一样,都两者定义在不同的命名空间下,是不同的方法。PS:LINQ定义在System.Linq中,EFCore定义在Microsoft.EntityFrameworkCore中

    2、我们将集合操作的扩展方法,划分为两类:①非立即执行方法,如Where、OrderBy、Select、GroupBy、Skip、Take、Include等;②立即执行方法:如Min、Max、Count、Sum、ToArray、ToList、foreach等。

    3、当执行非立即方法时,LINQ返回IEnumerable集合,EFCore返回IQueryable集合。两者最大区别为:LINQ会立即在服务器内存中执行计算(客户端评估);而EFCore会延迟执行,只有当我们执行立即执行方法后,EFCore才会将之前定义的所有非立即执行方法,整合为SQL抛到数据库执行(服务端评估)。

    4、利用EFCore中IQueryable的特点,我们就可以充分利用客户端评估和服务端评估,达到延迟执行、简化代码、复用代码、平衡性能等目的

    复制代码
    //LINQ返回IEnumerable
    var nums = new int[] { 1, 2, 3, 4 };
    var numsNew = nums.Where(n => n > 2);
    
    //EFCore返回IQueryable
    using var ctx = new MyDbContext();
    var books= ctx.Book.Where(a => a.Id > 0);
    复制代码

     

     

    二、IQueryable的延迟执行案例

    复制代码
    //利用IQueryable延迟执行,拼接复杂查询
    
    //定义一个复杂查询的方法,接受参数①关键词;②是否同时匹配书名和作者名;③是否按价格排序;④最高价格
    void QueryBooks(string searchWords, bool searchAll, bool orderByPrice, double upperPrice)
    {
        using var ctx = new MyDbContext();
    
        //查询低于最高价
        var books = ctx.Books.Where(b => b.Price <= upperPrice);
    
        //同时匹配书名和作者
        if(searchAll)
        {
            books = books.Where(b => b.Title.Contains(searchWords) || b.AuthorName.Contains(searchWords));
        }
        //只匹配书名
        else
        {
            books = books.Where(b =>b.Title.Contains(searchWords));
        }
    
        //按照价格排序
        if(orderByPrice)
        {
            books = books.OrderBy(b => b.Price);
        }
    
        //立即执行方法,遍历
        foreach(var item in books)
        {
            Console.WriteLine($"书名:{item.Title},作者:{item.AuthorName}");
        }
    }
    
    
    //调用方法
    QueryBooks("LINQ", true, true, 30);  //查询书名或作者名,按价格排序
    QueryBooks("LINQ", false, false, 50); //只查询书名,不按价格排序
    复制代码

     

     

    三、复用IQueryable的案例

    复制代码
    //获得一个IQueryable集合books,并三次复用它
    var books = ctx.Books.Where(b => b.Price >=20);
    
    //使用books集合,执行一次立即查询
    Console.WriteLine(books.Count());
    
    //再次使用books集合,执行第二次立即查询
    Console.WriteLine(books.Max(b => b.Price));
    
    //第三次立即查询
    foreach (var item in books.Where(b => b.PubTime.Year > 2000))
    {
        Console.WriteLine(item.Title);
    }
    复制代码

     

     

    四、结合使用服务端评估和客户端评估的案例

    复制代码
    //使用立即执行方法ToList,执行SQL查询(服务端评估),将结果存到服务器的内存中
    var books = await ctx.Books.Take(100000).ToListAsync();
    
    //使用服务器内存中的集合books,进行遍历查询,在服务器上执行(客户端评估)
    foreach (var item in books)
    {
        Console.WriteLine(item.Title);
    }
    
    //由于遍历条数比较多,需要一定时间
    //如果在遍历过程中,我们关闭数据库服务器,程序仍然可以正常进行
    //说明遍历前,已经将数据下载到客户端
    
    
    
    //大多数情况下,我们应该复用IQueryable,但在方法返回IQueryable,或嵌套遍历不同的DbSet时,需要考虑特别注意
    
    //出错情况1:方法返回IQueryable
    //方法中返回IQueryable时,会销毁上下文
    //正确应该返回:return ctx.Books.Where(b => b.Id>5).ToList();
    IQueryable QueryBooks()
    {
        using var ctx = MyDbContext();
        return ctx.Books.Where(b => b.Id>5);
    }
    
    foreach(var item in QueryBooks())
    {
        Console.WriteLine(item.Title);
    }
    
    //出错情况2:嵌套遍历不同的DbSet
    //嵌套循环,导致两个DataReader执行,大多数数据库不允许多个DataReader同时执行
    var books = ctx.Books.Where(b => b.Id > 1);
    foreach(var item1 in books)
    {
        Console.WriteLine(item1.Title);
        foreach(var item2 in ctx.Authors)
            {
                Console.WriteLive(item2.Id);
            }
    }
    复制代码

     

     

    五、最后一个综合案例:分页查询

    复制代码
    //定义一个分页查询方法,参数为获取第几页-pageIndex,每页显示几条-pageSize
    void OutputPage(int pageIndex, int pageSize)
    {
        using var ctx = new MyDbContext();
    
        //获取IQueryable集合books
        var books = ctx.Books(); 
    
        //复用books,计算集合总条数。LongCount方法和Count的功能一样
        long count = books.LongCount(); 
    
        //按每页显示条数pageSize,计算总页数
        //使用了Math的Ceiling方法,如有小数,取天花板值,最后转换类型为long
        long pageCount = (long)Math.Ceiling(count * 1.0 / pageSize);
        Console.WriteLine($"总页数:{pageCount}");
    
        //复用books,获取指定页码的数据,并遍历
        //使用了Skip和Take方法
        var pageIndexBooks = books.Skip((pageIndex - 1) * pageSize).Take(pageSize);
        foreach( var item in pageIndexBooks)
        {
            Console.WriteLine(item.Title)
        }
    }
    
    
    //调用方法
    OutputPage(1,10); //第1页,每页显示10条
    OutputPage(3,5); //第3页,每页显示5条
    复制代码

     

     

    特别说明:
    1、本系列内容主要基于杨中科老师的书籍《ASP.NET Core技术内幕与项目实战》及配套的B站视频视频教程,同时会增加极少部分的小知识点
    2、本系列教程主要目的是提炼知识点,追求快准狠,以求快速复习,如果说书籍学习的效率是视频的2倍,那么“简读系列”应该做到再快3-5倍

     

  • 相关阅读:
    Vue.prototype详解
    PLC相关的
    【iOS开发-AFNetWorking下的POST和GET】
    线性数据结构集会
    jenkins目录下的vue3项目——pnpm install后运行报错——奇葩问题解决
    多行多列按钮多选1
    Linux多线程(读者写者问题与自旋锁)
    .NET 8发布首个RC,比.NET 7的超级快更快
    GPT-4o:引领人工智能新时代的强大引擎
    Python自动化操作:简单、有趣、高效!解放你的工作流程!
  • 原文地址:https://www.cnblogs.com/functionMC/p/16838497.html