如何在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
相关产品推荐
相关产品推荐

