EFCore-分页查询

EFCore-分页查询

在之前我们了解到,在IQuryable和IEnumerable中,where的方法是在不同的接口实现的

IEnumerable是在内存中过滤(客户端评估)

IQuryable是把操作翻译成SQL语句(服务端评估)

所以在实际开发中,我们不会使用到IEnumerable接口来接受数据库查询的数据


延迟执行

IQuryable支持延迟执行,即不立即执行

它只是一个“可以放到数据库服务器去执行的查询”,他没有立即执行,只是“可以被执行”而已

对于IQueryable接口调用非终结方法的时候不会执行查询,调用终结方法的时候会立即执行查询

这样延迟查询的好处,可以动态拼接查询条件,根据不同的查询条件编译不同的SQL语句

因为这种延迟查询的缘故,所以不能将IQueryable对象传到DbContext销毁外执行

非终结方法终结方法
GroupBy()遍历
OrderBy()ToArray()
Include()ToList()
Skip()Min()/Max()
Take()Count()

一个方法的返回值类型如果是IQueryable类型,则为非终结方法,反之则为终结方法

需要注意的是:非终结方法没有异步方法,而终结方法普遍都存在异步方法
但一般来讲,遍历和ToList,ToArray方法没必要使用异步方法


对象复用

通过非终结方法生成的IQuryable对象可以通过不同的终结方法来编译成不同的SQL语句


分页查询

通过对IQuryable的复用和非终结方法Skip()以及Take()来实现分页查询功能

/// <summary>
/// 此方法用于返回一页数据和总页数
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
static void PrintPage(int pageIndex, int pageSize)
{
    using (var dbContext = new DBContext())
    {
        // 这时,程序还未向数据库发送SQL
        var queryable = dbContext.Articles.Select(a => a);

        // 跳过 当前页码-1 *每页条数(跳过的数据总条数)再拿一页数据
        var data = queryable.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

        // queryable的复用,在用一次计算总条数
        long count = queryable.LongCount();
        
        // 计算总页数 使用Math.Ceiling方法向上取整
        long pageCount = (long)Math.Ceiling(count / (double)pageSize);
    }
}

至此,拿到分页五大件


IQuryable底层

EFCore可以理解为是针对与ADO.NET的封装

在ADO.NET中,存在DataReader和DataTable对象

  • DataReader:分批从数据库读取数据,内存占用小,DB链接占用时间长
  • DataTable:把所有数据一次性的从数据库中加载到内存中,内存占用大,DB占用链接短

而IQueryable则实现了DataReader,节省了客户端内存,如果处理的慢,就会长时间占用数据库连接

如果想要一次性的将数据加载到内存,则使用IQueryable的ToArray()、ToArrayAsync()、ToList()、ToListAsync()方法

而因为DataReader的特性,是不允许多个DataReader同时执行的,所以不能对IQueryable进行嵌套遍历