从 Skip Take 到 Keyset:C# 分页原理与实践

简介

分页基础概念

  • 核心分页参数
public class PaginationParams
{
   
   
    private const int MaxPageSize = 100; // 最大每页条数限制
    
    public int PageNumber {
   
    get; set; } = 1;    // 当前页码(从1开始)
    
    private int _pageSize = 10;                 // 每页记录数
    public int PageSize
    {
   
   
        get => _pageSize;
        set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value;
    }
}
  • 分页结果封装
public class PagedResult<T>
{
   
   
    public int CurrentPage {
   
    get; set; }
    public int PageSize {
   
    get; set; }
    public int TotalCount {
   
    get; set; }
    public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
    public List<T> Items {
   
    get; set; } = new List<T>();
}

普通分页(Offset & Limit)

为什么普通分页会变慢?

page * size 很大时,数据库仍要扫描并丢弃前面 N 条记录,IO 和排序开销显著,上千页甚至秒级以上延迟。此时可考虑「高性能分页」。

原理
  • 使用数据库的 OFFSET … FETCH(SQL Server/PostgreSQL/SQLite)或 LIMIT … OFFSET(MySQL)进行分页。

  • 优点:实现简单、支持任意页码跳转。

  • 缺点:当页码(offset)较大时,数据库仍需扫描并跳过大量行,性能下降明显。

EF Core 实现
public async Task<PagedResult<Product>> GetProductsAsync(PaginationParams pagination)
{
   
   
    var query = _context.Products
        .Where(p => p.IsActive)
        .OrderBy(p => p.Name);
    
    var totalCount = await query.CountAsync();
    
    var items = await query
        .Skip((pagination.PageNumber - 1) * pagination.PageSize)
        .Take(pagination.PageSize)
        .ToListAsync();
    
    return new PagedResult<Product>
    {
   
   
        CurrentPage = pagination.PageNumber,
        PageSize = pagination.PageSize,
        TotalCount = totalCount,
        Items = items
    };
}
FreeSql 实现
public PagedResult<Product> GetProducts(PaginationParams pagination)
{
   
   
    var query = _fsql.Select<Product>()
        .Where(p => p.IsActive)
        .OrderBy(p => p.Name);
    
    var totalCount = query.Count();
    
    var items = query
        .Skip((pagination.PageNumber - 1) * pagination.PageSize)
        .Take(pagination.PageSize)
        .ToList();
    
    return new PagedResult<Product>
    {
   
   
        CurrentPage = pagination.PageNumber,
        PageSize = pagination.PageSize,
        TotalCount = totalCount,
        Items = items
    };
}
LinqToDB 实现
public PagedResult<Product> GetProducts(PaginationParams pagination)
{
   
   
    using (var db = new AppDbContext())
    {
   
   
        var query = db.Products
            .Where(p => p.IsActive)
            .OrderBy(p => p.Name);
        
        var totalCount = query.Count();
        
        var items = query
            .Skip((pagination.PageNumber - 1) * pagination.PageSize)
            .Take(pagination.PageSize)
            .ToList();
        
        return new PagedResult<Product>
        {
   
   
            CurrentPage = pagination.PageNumber,
            PageSize <
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值