7

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

 1 year ago
source link: https://www.cnblogs.com/functionMC/p/16838497.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

快读《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<T>、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<Book> 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倍


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK