02020502 EF Core高级02-IQuerable会延迟执行、分部和动态构建IQuerable、IQuerable的复用
02020502 EF Core高级02-IQuerable会延迟执行、分部和动态构建IQuerable、IQuerable的复用
1. IQuerable会延迟执行(视频3-25)
1、测试一下:只查询,但是不遍历IQueryable,查看是否有执行SQL语句。
2、在查询之后、foreach前后分别加上输出语句,查看输出内容的顺序。
3、发现:只有遍历IQueryable的时候才会执行。
1.1 IQuerable使用foreach循环对比
- 在02020409章5.2节基础上继续
// 1. 使用foreach循环
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts = ctx.Articles;
foreach (var item in arts)
{
Console.WriteLine(item.Title);
}
}
Console.ReadLine();
}
}
}
控制台输出:
杨中科入选中科院
微软发布.NET 10.0
微软发射微星
中国发射小行星探测器
// 查看SQL语句
SELECT t.Id, t.Message, t.Title
FROM T_Articles AS t),
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 2. 不使用foreach循环
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts = ctx.Articles;
}
Console.ReadLine();
}
}
}
注意,此时看不到SQL语句。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 2. 不使用foreach循环
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts = ctx.Articles;
}
Console.ReadLine();
}
}
}
注意,此时看不到SQL语句。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
Console.WriteLine("开始查询前");
IQueryable<Article> arts = ctx.Articles; // @1 代码先查询
Console.WriteLine("开始froeach");
foreach (var item in arts) // @1.2 代码后foreach
{
Console.WriteLine(item.Title);
}
Console.WriteLine("完成foreach");
}
Console.ReadLine();
}
}
}
控制台输出:
开始查询前
开始froeach // @2.1 日志先foreach
SELECT t.Id, t.Message, t.Title // 2.2 日志后查询
FROM T_Articles AS t),
杨中科入选中科院
微软发布.NET 10.0
微软发射微星
中国发射小行星探测器
完成foreach
说明:
1. SQL的Select语句在“开始froeach”之后。
2. 代码中先查询,后froeach;日志中先foreach再查询。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
总结:只有遍历IQuerable的时候才会执行SQL语句。IQuerable只是构建了一个可以被执行的查询。
1.2 IQuerable简介
1、IQueryable只是代表一个“可以放到数据库服务器去执行的查询”,它没有立即执行,只是“可以被执行”而已。
2、对于IQueryable接口调用非终结方法的时候不会执行查询,而调用终结方法的时候则会立即执行查询。
3、终结方法:遍历、ToArray()、ToList()、Min()、Max()、Count()等;
4、非终结方法:GroupBy()、OrderBy()、Include()、Skip()、Take()等。
5、简单判断:一个方法的返回值类型如果是IQueryable类型,那么这个方法一般就是非终结方法,否则就是终结方法。
1.3 为什么要延迟执行
1、可以在实际执行之前,分步构建IQueryable。
2、比如:定义一个方法根据给定的关键字searchWords来查询匹配的书;如果searchAll参数是true,则书名或者作者名中含有给定的searchWords都匹配,否则只匹配书名;如果orderByPrice参数为true,则按照价格排序,否则就自然排序;upperPrice参数代表价格上限。
void QueryBooks(string searchWords, bool searchAll, bool orderByPrice,double upperPrice)
3、试着传递不同参数,查看生成的SQL的不同。
1.4 分部构建IQuerable
- 在02020409章5.2节基础上继续
// 分部构建IQuerable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts01 = ctx.Articles.Where(a => a.Id > 1);
IQueryable<Article> arts02 = arts01.Skip(2);
IQueryable<Article> arts03 = arts02.Take(2);
IQueryable<Article> arts04 = arts03.Where(a => a.Title.Contains("微软"));
foreach (var item in arts04)
{
Console.WriteLine(item.Title);
}
}
Console.ReadLine();
}
}
}
查看SQL语句:
SELECT [t0].[Id], [t0].[Message], [t0].[Title]
FROM (
SELECT [t].[Id], [t].[Message], [t].[Title]
FROM [T_Articles] AS [t]
WHERE [t].[Id] > CAST(1 AS bigint)
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY
) AS [t0]
WHERE [t0].[Title] LIKE N'%微软%'
说明:把一步步返回的数据通过SQL语言串起来了。只要没有执行终结方法,那么可以将生成的SQL语句串起来。
1.5 动态构建IQuerable
- 在02020409章5.2节基础上继续
// Article.cs
using System.Collections.Generic;
namespace OneToMany
{
class Article // 文章
{
public long Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
public List<Comment> Comments { get; set; } = new List<Comment>(); // 建一个空的list,而不用默认的null。
public int Price { get; set; } // 增加价格
}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 迁移数据库
PM> add-migration init
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Applying migration '20250926124125_adddog'.
Done.
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs传参形式1
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
QueryArticles("微软", true, true, 80);
}
Console.ReadLine();
}
static void QueryArticles(string searchWords, bool searchAll, bool orderByPrice, double upperPrice)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts = ctx.Articles.Where(a => a.Price <= upperPrice);
if(searchAll)
{
arts = arts.Where(a => a.Title.Contains(searchWords) || a.Message.Contains(searchWords));
}
else
{
arts = arts.Where(a => a.Title.Contains(searchWords));
}
if(orderByPrice)
{
arts = arts.OrderBy(a => a.Price);
}
foreach (var item in arts)
{
Console.WriteLine(item.Title);
}
}
}
}
}
控制台输出:
微软发布.NET 10.0
微软发射微星
// 查看SQL语句
SELECT t.Id, t.Message, t.Price, t.Title
FROM T_Articles AS t
WHERE (CAST(t.Price) AS float) <= @__upperPrice_0) && (((@__searchWords_1 LIKE N'') || (CHARINDEX(@__searchWords_1, t.Title) > 0)) || ((@__searchWords_1 LIKE N'') || (CHARINDEX(@__searchWords_1, t.Message) > 0)))
ORDER BY t.Price ASC),
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs传参形式2
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
QueryArticles("微软", false, false, 80);
}
Console.ReadLine();
}
static void QueryArticles(string searchWords, bool searchAll, bool orderByPrice, double upperPrice)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts = ctx.Articles.Where(a => a.Price <= upperPrice);
if(searchAll)
{
arts = arts.Where(a => a.Title.Contains(searchWords) || a.Message.Contains(searchWords));
}
else
{
arts = arts.Where(a => a.Title.Contains(searchWords));
}
if(orderByPrice)
{
arts = arts.OrderBy(a => a.Price);
}
foreach (var item in arts)
{
Console.WriteLine(item.Title);
}
}
}
}
}
控制台输出:
微软发布.NET 10.0
微软发射微星
// SQL语句
SELECT t.Id, t.Message, t.Price, t.Title
FROM T_Articles AS t
WHERE (CAST(t.Price) AS float) <= @__upperPrice_0) && ((@__searchWords_1 LIKE N'') || (CHARINDEX(@__searchWords_1, t.Title) > 0))),
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
总结:IQuerable的延迟执行特性,使得动态的拼接查询条件变得简单。
1.6 IQuerable总结
- IQueryable代表一个对数据库中数据进行查询的一个逻辑,这个查询是一个延迟查询。我们可以调用非终结方法向IQueryable中添加查询逻辑,当执行终结方法的时候才真正生成SQL语句来执行查询。
- 可以实现以前要靠SQL拼接实现的动态查询逻辑。
2. IQuerable的复用(视频3-26)
- 在本章1.5小结基础上继续
1、IQueryable是一个待查询的逻辑,因此它是可以被重复使用的。
2、IQueryable<Book> books = ctx.Books.Where(b => b.Price <= 8);
Console.WriteLine(books.Count());
Console.WriteLine(books.Max(b=>b.Price));
var books2 = books.Where(b=>b.PubTime.Year>2000);
查看生成的SQL
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OneToMany
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
IQueryable<Article> arts = ctx.Articles.Where(a => a.Id > 1);
Console.WriteLine(arts.Count());
Console.WriteLine(arts.Max(a => a.Price));
IQueryable<Article> arts2 = arts.Where(a => a.Title.Contains("微软"));
arts.ToList(); // 终结方法
}
Console.ReadLine();
}
}
}
// SQL语句
SELECT COUNT(*)
FROM [T_Articles] AS [t]
WHERE [t].[Id] > CAST(1 AS bigint)
SELECT MAX([t].[Price])
FROM [T_Articles] AS [t]
WHERE [t].[Id] > CAST(1 AS bigint)
...
说明:IQuerable可以复用,在不同的方法里面调用终结方法,或者用IQuerable构建新的IQuerable的时候,IQuerable是可以复用的。
结尾
书籍:ASP.NET Core技术内幕与项目实战
视频:https://www.bilibili.com/video/BV1pK41137He
著:杨中科
ISBN:978-7-115-58657-5
版次:第1版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※