using System;
using System.Collections.Generic;
using System.Linq;
using MessagePack;

namespace Dpz.Core.Web.Pager;

/// <summary>
/// 表示包含分页数据对象的强类型列表。
/// </summary>
/// <typeparam name="T"></typeparam>
[MessagePackObject(keyAsPropertyName: true)]
public class PagedList<T> : List<T>, IPagedList<T>
{
    /// <summary>
    /// 使用要分页的所有数据项、当前页索引和每页显示的记录数初始化PagedList对象
    /// </summary>
    /// <param name="allItems">要分页的所有数据项</param>
    /// <param name="pageIndex">当前页索引</param>
    /// <param name="pageSize">每页显示的记录数</param>
    public PagedList(IEnumerable<T> allItems, int pageIndex, int pageSize)
    {
        PageSize = pageSize;
        var items = allItems as IList<T> ?? allItems.ToList();
        TotalItemCount = items.Count();
        CurrentPageIndex = pageIndex;
        AddRange(items.Skip(StartItemIndex - 1).Take(pageSize));
    }

    /// <summary>
    /// 使用当前页数据项、当前页索引、每页显示的记录数和要分页的总记录数初始化PagedList对象
    /// </summary>
    /// <param name="currentPageItems">当前页数据项</param>
    /// <param name="pageIndex">当前页索引</param>
    /// <param name="pageSize">每页显示的记录数</param>
    /// <param name="totalItemCount">要分页数据的总记录数</param>
    public PagedList(
        IEnumerable<T> currentPageItems,
        int pageIndex,
        int pageSize,
        int totalItemCount
    )
    {
        AddRange(currentPageItems);
        TotalItemCount = totalItemCount;
        CurrentPageIndex = pageIndex;
        PageSize = pageSize;
    }

    /// <summary>
    /// 使用要分页的所有数据项、当前页索引和每页显示的记录数初始化PagedList对象
    /// </summary>
    /// <param name="allItems">要分页的所有数据项</param>
    /// <param name="pageIndex">当前页索引</param>
    /// <param name="pageSize">每页显示的记录数</param>
    public PagedList(IQueryable<T> allItems, int pageIndex, int pageSize)
    {
        var startIndex = (pageIndex - 1) * pageSize;
        AddRange(allItems.Skip(startIndex).Take(pageSize));
        TotalItemCount = allItems.Count();
        CurrentPageIndex = pageIndex;
        PageSize = pageSize;
    }

    /// <summary>
    /// 使用当前页数据项、当前页索引、每页显示的记录数和要分页的总记录数初始化PagedList对象
    /// </summary>
    /// <param name="currentPageItems">当前页数据项</param>
    /// <param name="pageIndex">当前页索引</param>
    /// <param name="pageSize">每页显示的记录数</param>
    /// <param name="totalItemCount">要分页数据的总记录数</param>
    public PagedList(
        IQueryable<T> currentPageItems,
        int pageIndex,
        int pageSize,
        int totalItemCount
    )
    {
        AddRange(currentPageItems);
        TotalItemCount = totalItemCount;
        CurrentPageIndex = pageIndex;
        PageSize = pageSize;
    }

    /// <summary>
    /// 当前页索引
    /// </summary>
    [IgnoreMember]
    public int CurrentPageIndex { get; set; }

    /// <summary>
    /// 每页显示的记录数
    /// </summary>
    [IgnoreMember]
    public int PageSize { get; set; }

    /// <summary>
    /// 要分页的数据总数
    /// </summary>
    [IgnoreMember]
    public int TotalItemCount { get; set; }

    /// <summary>
    /// 总页数
    /// </summary>
    [IgnoreMember]
    public int TotalPageCount => (int)Math.Ceiling(TotalItemCount / (double)PageSize);

    /// <summary>
    /// 开始记录索引
    /// </summary>
    [IgnoreMember]
    public int StartItemIndex => (CurrentPageIndex - 1) * PageSize + 1;

    /// <summary>
    /// 是否有上一页
    /// </summary>
    [IgnoreMember]
    public bool HasNextPage => CurrentPageIndex < TotalPageCount;

    /// <summary>
    /// 结束记录索引
    /// </summary>
    [IgnoreMember]
    public int EndItemIndex =>
        TotalItemCount > CurrentPageIndex * PageSize ? CurrentPageIndex * PageSize : TotalItemCount;

    public static PagedList<T> Empty() => new(new List<T>(), 0, 0);

    public PagedList()
    {
        AddRange([]);
        CurrentPageIndex = 0;
        PageSize = 0;
    }

    public PagedListWarp<T> ToPagedListWarp()
    {
        return new PagedListWarp<T>(CurrentPageIndex, PageSize, TotalItemCount, this);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个用于分页功能的泛型类 PagedList<T>,它实现了分页数据的封装和管理。让我详细解释一下这个代码:

类的基本结构

[MessagePackObject(keyAsPropertyName: true)]
public class PagedList<T> : List<T>, IPagedList<T>
  • 继承自 List<T>,所以它本身就是一个列表,可以存储当前页的数据项
  • 实现了 IPagedList<T> 接口
  • 使用了 MessagePackObject 特性,支持序列化

构造函数

提供了4个重载构造函数和1个无参构造函数:

1. 从所有数据中分页(IEnumerable)

public PagedList(IEnumerable<T> allItems, int pageIndex, int pageSize)
  • 接收所有数据,然后在内存中进行分页
  • 使用 Skip 和 Take 方法截取当前页数据

2. 直接提供当前页数据(IEnumerable)

public PagedList(IEnumerable<T> currentPageItems, int pageIndex, int pageSize, int totalItemCount)
  • 直接接收已经分页好的当前页数据
  • 适用于已经在数据库层面完成分页的场景

3. 从查询中分页(IQueryable)

public PagedList(IQueryable<T> allItems, int pageIndex, int pageSize)
  • 针对数据库查询优化,延迟执行分页
  • 先执行 Skip 和 Take,最后才执行 Count()

4. 直接提供当前页查询(IQueryable)

public PagedList(IQueryable<T> currentPageItems, int pageIndex, int pageSize, int totalItemCount)

核心属性

所有属性都标记了 [IgnoreMember],表示在序列化时会被忽略:

  • CurrentPageIndex: 当前页索引
  • PageSize: 每页显示的记录数
  • TotalItemCount: 总记录数
  • TotalPageCount: 总页数(计算属性)
  • StartItemIndex: 当前页开始记录的索引
  • EndItemIndex: 当前页结束记录的索引
  • HasNextPage: 是否有下一页

计算逻辑

// 总页数计算
public int TotalPageCount => (int)Math.Ceiling(TotalItemCount / (double)PageSize);

// 开始索引计算(从1开始)
public int StartItemIndex => (CurrentPageIndex - 1) * PageSize + 1;

// 结束索引计算
public int EndItemIndex => 
    TotalItemCount > CurrentPageIndex * PageSize ? 
    CurrentPageIndex * PageSize : TotalItemCount;

工具方法

  • Empty(): 创建一个空的分页列表
  • ToPagedListWarp(): 转换为包装对象

使用场景

这个类特别适用于:

  1. Web应用的分页显示
  2. API返回分页数据
  3. 需要在内存中对数据进行分页处理
  4. 数据库查询结果的分页封装

设计优点

  1. 灵活性: 支持多种数据源(IEnumerable、IQueryable)
  2. 性能优化: 对IQueryable的处理考虑了延迟执行
  3. 易用性: 继承自List,可以直接当作列表使用
  4. 完整性: 提供了分页所需的所有计算属性
  5. 序列化支持: 支持MessagePack序列化

这是一个设计良好的分页工具类,能够满足大多数分页场景的需求。

评论加载中...