You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在EF的LINQ查询中对Include关联列表做IQueryable状态分页

解决EF中关联集合的数据库层面分页问题

直接用Include没法对关联的Books做数据库层面分页,因为Include会加载该作者的全部书籍。要实现IQueryable层面的分页,有两种靠谱的方案:

方案1:投影查询(推荐)

直接通过投影构造包含分页书籍的对象,EF会自动生成带分页逻辑的SQL,完全在数据库端完成操作:

var pageNumber = 1;
var pageSize = 10;

var authorWithPagedBooks = await _dbContext.Authors
    .Where(a => a.Id == id)
    .Select(a => new 
    {
        Author = a,
        PagedBooks = a.Books
            .OrderBy(b => b.PublishDate) // 必须先排序,分页才有意义
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
    })
    .FirstOrDefaultAsync();

// 从匿名对象中取出数据
var author = authorWithPagedBooks?.Author;
var pagedBooks = authorWithPagedBooks?.PagedBooks;

如果需要返回强类型,可以定义一个DTO类:

public class AuthorWithPagedBooksDto
{
    public Author Author { get; set; }
    public List<Book> PagedBooks { get; set; }
}

// 查询时替换为DTO
var authorDto = await _dbContext.Authors
    .Where(a => a.Id == id)
    .Select(a => new AuthorWithPagedBooksDto
    {
        Author = a,
        PagedBooks = a.Books
            .OrderBy(b => b.PublishDate)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToList()
    })
    .FirstOrDefaultAsync();

方案2:显式加载

先获取Author实体,再通过EF的Entry API显式分页加载关联的Books,同样是数据库层面的操作:

var pageNumber = 1;
var pageSize = 10;

// 先获取作者,不加载Books
var author = await _dbContext.Authors
    .FirstOrDefaultAsync(a => a.Id == id);

if (author != null)
{
    // 显式分页加载Books
    await _dbContext.Entry(author)
        .Collection(a => a.Books)
        .Query()
        .OrderBy(b => b.PublishDate)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .LoadAsync();
}

注意事项

  • 分页前必须先排序,否则数据库返回的结果顺序不确定,分页会出现数据混乱的情况。
  • 两种方案都不会把全部Books加载到内存,都是在数据库端完成筛选和分页,符合性能优化需求。

内容的提问来源于stack exchange,提问作者Fatlum Maliqi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:35:21