using Mapster;

namespace Dpz.Core.Service.Mediator;

public static class PageExtensions
{
    /// <param name="source">包含所有要分页数据的IMongoQueryable对象。</param>
    /// <typeparam name="TSource">原类型</typeparam>
    extension<TSource>(IQueryable<TSource> source)
    {
        /// <summary>
        /// 根据当前页索引pageIndex及每页记录数pageSize获取要分页的ViewModel数据对象。
        /// </summary>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="pageIndex">当前页索引。</param>
        /// <param name="pageSize">每页显示的记录数。</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<IPagedList<TDestination>> ToPagedListAsync<TDestination>(
            int pageIndex,
            int pageSize,
            CancellationToken cancellationToken = default
        )
        {
            if (pageIndex < 1)
            {
                pageIndex = 1;
            }

            var itemIndex = (pageIndex - 1) * pageSize;
            var totalItemCount = await source.CountAsync(cancellationToken: cancellationToken);
            while (totalItemCount <= itemIndex && pageIndex > 1)
            {
                itemIndex = (--pageIndex - 1) * pageSize;
            }

            var pageOfItems = source.Skip(itemIndex).Take(pageSize);
            var data = (await pageOfItems.ToListAsync(cancellationToken: cancellationToken)).Adapt<
                List<TDestination>
            >();
            return new PagedList<TDestination>(data, pageIndex, pageSize, totalItemCount);
        }
    }

    extension<TSource>(IQueryable<TSource> source)
    {
        public async Task<List<TDestination>> ToListAsync<TDestination>(
            CancellationToken cancellationToken = default
        )
        {
            var data = await source.ToListAsync(cancellationToken: cancellationToken);
            return data.Adapt<List<TDestination>>();
        }

        public async Task<IPagedList<TSource>> ToPagedListAsync(
            int pageIndex,
            int pageSize,
            CancellationToken cancellationToken = default
        )
        {
            if (pageIndex < 1)
            {
                pageIndex = 1;
            }

            var itemIndex = (pageIndex - 1) * pageSize;
            var totalItemCount = await source.CountAsync(cancellationToken: cancellationToken);
            while (totalItemCount <= itemIndex && pageIndex > 1)
            {
                itemIndex = (--pageIndex - 1) * pageSize;
            }

            var pageOfItems = source.Skip(itemIndex).Take(pageSize);
            var data = await pageOfItems.ToListAsync(cancellationToken: cancellationToken);
            return new PagedList<TSource>(data, pageIndex, pageSize, totalItemCount);
        }

        public async Task<IPagedList<TDestination>> ToPagedListAsync<TDestination>(
            int pageIndex,
            int pageSize,
            Func<TSource, TDestination> func,
            CancellationToken cancellationToken = default
        )
        {
            if (pageIndex < 1)
            {
                pageIndex = 1;
            }

            var itemIndex = (pageIndex - 1) * pageSize;
            var totalItemCount = await source.CountAsync(cancellationToken: cancellationToken);
            while (totalItemCount <= itemIndex && pageIndex > 1)
            {
                itemIndex = (--pageIndex - 1) * pageSize;
            }

            var pageOfItems = source.Skip(itemIndex).Take(pageSize);
            var data = await pageOfItems.ToListAsync(cancellationToken: cancellationToken);
            return new PagedList<TDestination>(
                data.Select(func),
                pageIndex,
                pageSize,
                totalItemCount
            );
        }
    }

    extension<TSource>(IFindFluent<TSource, TSource> source)
    {
        public async Task<PagedList<TDestination>> ToPagedListAsync<TDestination>(
            int pageIndex,
            int pageSize,
            CancellationToken cancellationToken = default
        )
        {
            if (pageIndex < 1)
            {
                pageIndex = 1;
            }

            var itemIndex = (pageIndex - 1) * pageSize;
            var totalItemCount = checked((int)await source.CountDocumentsAsync(cancellationToken));
            while (totalItemCount <= itemIndex && pageIndex > 1)
            {
                itemIndex = (--pageIndex - 1) * pageSize;
            }

            var pageOfItems = source.Skip(itemIndex).Limit(pageSize);
            var data = (await pageOfItems.ToListAsync(cancellationToken: cancellationToken)).Adapt<
                List<TDestination>
            >();
            return new PagedList<TDestination>(data, pageIndex, pageSize, totalItemCount);
        }

        public async Task<PagedList<TSource>> ToPagedListAsync(
            int pageIndex,
            int pageSize,
            CancellationToken cancellationToken = default
        )
        {
            if (pageIndex < 1)
            {
                pageIndex = 1;
            }

            var itemIndex = (pageIndex - 1) * pageSize;
            var totalItemCount = checked((int)await source.CountDocumentsAsync(cancellationToken));
            while (totalItemCount <= itemIndex && pageIndex > 1)
            {
                itemIndex = (--pageIndex - 1) * pageSize;
            }

            var pageOfItems = source.Skip(itemIndex).Limit(pageSize);
            var data = await pageOfItems.ToListAsync(cancellationToken: cancellationToken);
            return new PagedList<TSource>(data, pageIndex, pageSize, totalItemCount);
        }
    }

    public static IPagedList<T> ToPagedList<T>(
        this IEnumerable<T> source,
        int pageIndex,
        int pageSize,
        int totalItem
    )
    {
        return new PagedList<T>(source, pageIndex, pageSize, totalItem);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个C#静态扩展类,提供了分页查询和对象映射的功能。主要用于简化数据查询时的分页和类型转换操作。

主要功能模块

1. IQueryable 扩展(第一组)

extension<TSource>(IQueryable<TSource> source)

提供 ToPagedListAsync<TDestination> 方法:

  • 功能:从 IQueryable 数据源获取分页数据,并使用 Mapster 将结果映射为目标类型
  • 参数:
    • pageIndex: 当前页码(从1开始)
    • pageSize: 每页记录数
    • cancellationToken: 取消令牌
  • 核心逻辑:
    • 页码校验(小于1时重置为1)
    • 计算跳过的记录数
    • 获取总记录数
    • 自动调整页码:如果计算的起始索引超出总记录数,自动回退到有效页
    • 使用 Skip 和 Take 实现分页
    • 使用 Mapster 的 Adapt 方法进行对象映射

2. IQueryable 扩展(第二组)

提供三个重载方法:

a. ToListAsync<TDestination>

  • 将查询结果转换为列表并映射为目标类型

b. ToPagedListAsync (无类型转换)

  • 返回原类型的分页数据,不进行对象映射

c. ToPagedListAsync<TDestination> (使用自定义转换函数)

  • 接受 Func<TSource, TDestination> 委托
  • 使用自定义函数进行类型转换(而非 Mapster)

3. IFindFluent<TSource, TSource> 扩展

专门为 MongoDB 查询设计的扩展方法:

两个重载方法:

  • ToPagedListAsync<TDestination>: 带类型映射
  • ToPagedListAsync: 不进行类型映射

关键差异:

  • 使用 CountDocumentsAsync 获取总数(MongoDB 特定)
  • 使用 Limit 代替 Take(MongoDB 语法)
  • 使用 checked 防止整数溢出

4. IEnumerable 扩展

public static IPagedList<T> ToPagedList<T>(...)
  • 用于已加载到内存的数据集合
  • 直接封装为分页对象,不执行异步查询

关键设计特点

  1. 智能页码调整:当请求的页码超出范围时自动回退到最后一页
  2. 类型映射集成:深度集成 Mapster 对象映射库
  3. 多数据源支持:同时支持 EF Core (IQueryable) 和 MongoDB (IFindFluent)
  4. 异步优先:所有数据库操作均为异步方法
  5. 取消令牌支持:所有异步方法支持操作取消

语法说明

代码使用了 C# 扩展方法的非标准语法 extension<T>,这可能是:

  • 某个预处理器或代码生成工具的语法
  • 自定义的语法糖
  • 标准写法应该是:public static ... 方法名(this IQueryable<TSource> source, ...)

依赖项

  • Mapster:对象映射库
  • Entity Framework Core:IQueryable.CountAsync、ToListAsync
  • MongoDB.Driver:IFindFluent、CountDocumentsAsync
评论加载中...