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※

※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

posted @ 2025-10-06 11:12  qinway  阅读(10)  评论(0)    收藏  举报