您现在的位置是:首页 >其他 >第16章 逻辑分页实现网站首页其他

第16章 逻辑分页实现

zhoujian_911 2024-06-26 14:23:30
简介第16章 逻辑分页实现

1 Core.Extensions.QueryableExtension

using System.Linq.Expressions;

namespace Core.Extensions

{

    /// <summary>

    /// Queryable扩展类--类】

    /// <remarks>

    /// 摘要:

    ///    “IQueryable”实例操作方法通过该类中相应方法成员进行了扩展,以提升 “IQueryable”实例操作方法的功能,

    /// 这些被扩展的方法包括:LongSkip OrderByOrderByDescending

    /// </remarks>

    /// </summary>

    public static class QueryableExtension

    {

        ///<typeparam name="TSource">泛型类型实例(1个指定类的类型实例)</typeparam>

        ///<param name="source">“IQueryable”实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)</param>

        ///<param name="LongCount">需要跳过项的总计值。</param>

        /// <summary>

        /// 【长整型跳过】

        /// <remarks>

        /// 摘要:

        ///    由于“System.Linq.Queryable.Skip”方法只支持类型为“int”“Int32”类型的需要跳过项的总计值,为了支持类型为“long”“Int64”的需要跳过项的总计值,从而定义了该扩展方法。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    “IQueryable”实例,该实例存储着1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)在执行过跳过项操作后所剩余的数据源。

        /// </returns>

        /// </summary>

        public static IQueryable<TSource> LongSkip<TSource>(this IQueryable<TSource> source, long LongCount)

        {

            //“Math.DivRem()”方法在 C#中的用于除法和计算两个数的商:“quotients”(取整操作,“quotients”必须被定义为“long”“Int64”类型,因为“quotients”的最大值远远大于“int.MaxValue”)

            //并在输出参数中返回余数:“remainder”(取余,“remainder”可以转换为“int”“Int32”类型,因为“remainder”的最大值一定小于“int.MaxValue”)

            long quotients = Math.DivRem(LongCount, int.MaxValue, out long remainder);

            //先执行从数据源中跳过quotients*int.MaxValue项操作。

            for (long i = 0; i < quotients; i += 1)

                source = source.Skip(int.MaxValue);

            //如果余数(“remainder”)不为:0,则在上一个操作的基础上,再执行跳过remainder项操作。

            if (remainder != 0)

                source = source.Skip((int)remainder);

            return source;

        }

        ///<typeparam name="TSource">泛型类型实例(1个指定类的类型实例)</typeparam>

        ///<param name="source">“IQueryable”实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)</param>

        ///<param name="propertyName">定类的指定属性成员名(或对应表中的相应字段名)</param>

        /// <summary>

        /// 【正序排序】

        /// <remarks>

        /// 摘要:

        ///    “System.Linq.Queryable.OrderBy”方法进行扩展,使用支持以指定类的指定属性成员名(或对应表中的相应字段名)字符串进行正序排序操作。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     “IQueryable”实例,该实例存储着1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)在执行过正序排序操作后的数据源。

        /// </returns>

        /// </summary>

        public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)

        {

            //如果用于排序操作的参数实例(指定类的指定属性成员名(或对应表中的相应字段名)为空 ,则直接返回原数据源。

            if (string.IsNullOrEmpty(propertyName))

            {

                return (IOrderedQueryable<TSource>)source;

            }

            //为指定类的排序操作构建相应的“lambda”表达式。

            //“lambda”表达式的指针

            var param = Expression.Parameter(typeof(TSource), "p");

            //“lambda”表达式的指针与指定类的指定属性成员(或对应表中的相应字段)实例的组合。

            var property = Expression.Property(param, propertyName);

            为指定类组合出完整的“lambda”表达式。

            var exp = Expression.Lambda(property, param);

            //委托方法参数实例,通过委托方法参数实例,生成一个指定的正序排序“lambda”表达式。

            MethodCallExpression resultExpression = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { source.ElementType, exp.Body.Type }, source.Expression, exp);

            //返回经过正序排序后的数据源。

            return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(resultExpression);

        }

        ///<typeparam name="TSource">泛型类型实例(1个指定类的类型实例)</typeparam>

        ///<param name="source">“IQueryable”实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)</param>

        ///<param name="propertyName">定类的指定属性成员名(或对应表中的相应字段名)</param>

        /// <summary>

        /// 【倒序排序】

        /// <remarks>

        /// 摘要:

        ///    “System.Linq.Queryable.OrderByDescending”方法进行扩展,使用支持以指定类的指定属性成员名(或对应表中的相应字段名)字符串进行倒序排序操作。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///      “IQueryable”实例,该实例存储着1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)在执行过倒序排序操作后的数据源。

        /// </returns>

        /// </summary>

        public static IOrderedQueryable<TSource> OrderByDescending<TSource>(this IQueryable<TSource> source, string propertyName)

        {

            //如果用于排序操作的参数实例(指定类的指定属性成员名(或对应表中的相应字段名)为空 ,则直接返回原数据源。

            if (string.IsNullOrEmpty(propertyName))

            {

                return (IOrderedQueryable<TSource>)source;

            }

            //为指定类的排序操作构建相应的“lambda”表达式。

            //“lambda”表达式的指针

            var param = Expression.Parameter(typeof(TSource), "p");

            //“lambda”表达式的指针与指定类的指定属性成员(或对应表中的相应字段)实例的组合。

            var property = Expression.Property(param, propertyName);

            为指定类组合出完整的“lambda”表达式。

            var exp = Expression.Lambda(property, param);

            //委托方法参数实例,通过委托方法参数实例,生成一个指定的倒序排序“lambda”表达式。

            MethodCallExpression resultExpression = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { source.ElementType, exp.Body.Type }, source.Expression, exp);

            //返回经过倒序排序后的数据源。

            return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(resultExpression);

        }

    }

}

2 Core.IPagedList<T>

namespace Core

{

    ///<typeparam name="T">泛型类型实例(1个指定类的类型实例)</typeparam>

    /// <summary>

    /// (逻辑)分页列表--接口】

    /// <remarks>

    /// 摘要:

    ///     通过继承该接口的具体实现类中的属性成员实例,只逻辑的从数据源中加载指定1页中的数据,存储到1(逻辑)页中,从而尽量少的从指定表加载数据,从而尽量降低内存的消耗。

    /// 说明:

    ///     (逻辑)分页交互操作对象是当前程序与数据库的指定表。

    /// </remarks>

    /// </summary>

    public interface IPagedList<T> : IList<T>

    {

        /// <summary>

        /// 【当前页】

        /// <remarks>

        /// 摘要:

        ///     获取/设置当前页,当前逻辑(内存)页面的长整型索引值,从“0”开始计算。

        /// </remarks>

        /// </summary>

        long PageIndex { get; }

        /// <summary>

        /// 【页面大小】

        /// <remarks>

        /// 摘要:

        ///     获取/设置页面大小,即1逻辑(内存)页面最多所存储的实例项的长整型个数值。

        /// </remarks>

        /// </summary>

        int PageSize { get; }

        /// <summary>

        /// 【总计值】

        /// <remarks>

        /// 摘要:

        ///     获取/设置数据源实例项的长整型总计值。

        /// </remarks>

        /// </summary>

        long TotalCount { get; }

        /// <summary>

        /// 【总页数】

        /// <remarks>

        /// 摘要:

        ///     获取/设置总页数,即数据源实例项,可以分为页数的总计值。

        /// </remarks>

        /// </summary>

        long TotalPages { get; }

       

        /// <summary>

        /// 【有上1页?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(没有)/true(),该值指示在逻辑(内存)分页操作中当前页是否有上1(1页之前没有上1)

        /// </remarks>

        /// </summary>

        bool HasPreviousPage { get; }

       

        /// <summary>

        /// 【有下1页?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(没有)/true(),该值指示在逻辑(内存)分页操作中当前页是否有下1(最后1页之后没有下1)

        /// </remarks>

        /// </summary>

        bool HasNextPage { get; }

    }

}

3 Core.PagedList<T>

using Core.Extensions;

namespace Core

{

    ///<typeparam name="T">泛型类型实例(1个指定类的类型实例)</typeparam>

    /// <summary>

    /// (逻辑)分页列表--类】

    /// <remarks>

    /// 摘要:

    ///     通过该类及其属性成员实例,只逻辑的从数据源中加载指定1页中的数据,存储到1(逻辑)页中,从而尽量少的从指定表加载数据,从而尽量降低内存的消耗。

    /// 说明:

    ///     (逻辑)分页交互操作对象是当前程序与数据库的指定表。

    /// </remarks>

    /// </summary>

    [Serializable]

    public partial class PagedList<T> : List<T>, IPagedList<T>

    {

        #region 拷贝构造方法

        /// <param name="source">以列表接口实例进行存储的1个指定类型的数据源(包含:数组、列表、字典等)</param>

        /// <param name="pageIndex">分页操作中当前页的长整型索引值。</param>

        /// <param name="pageSize">1逻辑(内存)页面最多所存储的实例项的长整型个数值。</param>

        /// <param name="totalCount">数据源实例项的长整型总计值。</param>

        /// <summary>

        /// 【拷贝构造方法】

        /// <remarks>

        /// 摘要:

        ///     通过拷贝构造方法为该类中的属性成员赋值实例化初始值,并获取1逻辑(内存)页面内的所有数据。

        /// </remarks>

        /// </summary>

        public PagedList(IList<T> source, long pageIndex, int pageSize, long? totalCount = null)

        {

            //分页操作中每页最多显示实例的项()数值,“Math.Max”方法保证该参数实例的最小值必须>=1

            pageSize = Math.Max(pageSize, 1);

            TotalCount = totalCount ?? source.Count;

            TotalPages = TotalCount / pageSize;

            if (TotalCount % pageSize > 0)

                TotalPages++;

            PageSize = pageSize;

            PageIndex = pageIndex;

            //  1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)1内存页面内的所有项1次性加载到列表实例中,

            //AddRange方法是1性把所有的数据加载的目录中,而不必像forforeach依次加载数据。

            AddRange(totalCount != null ? source : source.LongSkip(pageIndex * pageSize).Take(pageSize));

        }

        #endregion

        #region 属性

        /// <summary>

        /// 【当前页】

        /// <remarks>

        /// 摘要:

        ///     获取/设置当前页,当前逻辑(内存)页面的长整型索引值,从“0”开始计算。

        /// </remarks>

        /// </summary>

        public long PageIndex { get; }

        /// <summary>

        /// 【页面大小】

        /// <remarks>

        /// 摘要:

        ///     获取/设置页面大小,即1逻辑(内存)页面最多所存储的实例项的长整型个数值。

        /// </remarks>

        /// </summary>

        public int PageSize { get; }

        /// <summary>

        /// 【总计值】

        /// <remarks>

        /// 摘要:

        ///     获取/设置数据源实例项的长整型总计值。

        /// </remarks>

        /// </summary>

        public long TotalCount { get; }

        /// <summary>

        /// 【总页数】

        /// <remarks>

        /// 摘要:

        ///     获取/设置总页数,即数据源实例项,可以分为页数的总计值。

        /// </remarks>

        /// </summary>

        public long TotalPages { get; }

        /// <summary>

        /// 【有上1页?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(没有)/true(),该值指示在逻辑(内存)分页操作中当前页是否有上1(1页之前没有上1)

        /// </remarks>

        /// </summary>

        public bool HasPreviousPage => PageIndex > 0;

        /// <summary>

        /// 【有下1页?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(没有)/true(),该值指示在逻辑(内存)分页操作中当前页是否有下1(最后1页之后没有下1)

        /// </remarks>

        /// </summary>

        public bool HasNextPage => PageIndex + 1 < TotalPages;

        #endregion

    }

}

4 System.Linq. AsyncIQueryableExtensions(DataExtensionsAsyncIQueryableExtensions.cs)

using Core;

using Core.Extensions;

//Nuget

//Nuget--linq2db

using LinqToDB;

namespace System.Linq

{

    /// <summary>

    /// IQueryable异步扩展类--类】

    /// <remarks>

    /// 摘要:

    ///    “IQueryable”实例通过“linq2db”中间件中的异步方法,实现自身的异步操作,这些被扩展的异步方法包括:LongCountAsync ToListAsyncToPagedListAsync

    /// </remarks>

    /// </summary>

    public static class AsyncIQueryableExtensions

    {

        /// <typeparam name="T">泛型类型实例(1个指定类的类型实例)</typeparam>

        /// <param name="source">“IQueryable”实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)</param>

        /// <param name="pageIndex">当前页的页数值(“pageSize”结合,设定需要跳过指定实体实例的个数值)</param>

        /// <param name="pageSize">分页操作中每页最多显示实例的项()数值(“pageIndex”结合,设定需要跳过指定实体实例的个数值) </param>

        /// <param name="getOnlyTotalCount">指示数据源中否包含有实例,默认值:false,该方法使用的是非操作,即数据源中否包含有实例,如果无实例,则(new)新实例化1个逻辑分页实例。</param>

        /// <summary>

        /// 【异步分页列表】

        /// <remarks>

        /// 摘要:

        ///     以异步操作方式,只从数据源中加载指定1逻辑(内存)页中的数据,存储到“IPagedList(逻辑分页)”的实例中,从而尽量少的从指定表加载数据,从而尽量降低内存的消耗。

        /// </remarks>

        /// <returns>

        ///   “IPagedList(逻辑分页)”的实例,该实例存储着数据源中(符合指定查询条件的)所有实例。

        /// </returns>

        /// </summary>

        public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source, long pageIndex, int pageSize, bool getOnlyTotalCount = false)

        {

            if (source == null)

                return new PagedList<T>(new List<T>(), pageIndex, pageSize);

            //分页操作中每页最多显示实例的项()数值,“Math.Max”方法保证该参数实例的最小值必须>=1

            pageSize = Math.Max(pageSize, 1);

            var count = await source.CountAsync();

            var data = new List<T>();

            //  1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)1内存页面内的所有项1次性加载到列表实例中,

            //AddRange方法是1性把所有的数据加载的目录中,而不必像forforeach依次加载数据。

            if (!getOnlyTotalCount)

                data.AddRange(await source.LongSkip(pageIndex * pageSize).Take(pageSize).ToListAsync());

            return new PagedList<T>(data, pageIndex, pageSize, count);

        }

    }

}

5 Data.Repository<TEntity>.GetAllPagedAsync

 /// <param name="func">1个具有返回值的泛型委托方法实例,该泛型委托方法实例用于获取1个指定实体的1/n个实例。</param>

        /// <param name="pageIndex">当前页的页数值(“pageSize”结合,设定需要跳过指定实体实例的个数值),默认值:0,即不跳过。</param>

        /// <param name="pageSize">分页操作中每页最多显示实例的项()数值(“pageIndex”结合,设定需要跳过指定实体实例的个数值),默认值:int.MaxValue,即跳过int.MaxValue行数。 </param>

        /// <param name="getOnlyTotalCount">指示数据源中否包含有实例,默认值:false,该方法使用的是非操作,即数据源中否包含有实例,如果无实例,则(new)新实例化1个逻辑分页实例。</param>

        /// <param name="includeDeleted">指示是否包含指定实体中所有的逻辑删除项,默认值:true,即指定实体的所有实例。</param>

        /// <summary>

        /// 【异步分页列表】

        /// <remarks>

        /// 摘要:

        ///     以异步操作方式,只从数据源中加载指定1逻辑(内存)页中的数据,存储到“IPagedList(逻辑分页)”的实例中,从而尽量少的从指定表加载数据,从而尽量降低内存的消耗。

        ///  说明:

        ///     该方法没有定义缓存操作,更没有定义缓存的移除操作。

        /// </remarks>

        /// <returns>

        ///    “IPagedList(逻辑分页)”的实例,该实例存储着数据源中(符合指定查询条件的)所有实例。

        /// </returns>

        /// </summary>

        public virtual async Task<IPagedList<TEntity>> GetAllPagedAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> func = null,

          long pageIndex = 0, int pageSize = int.MaxValue, bool getOnlyTotalCount = false, bool includeDeleted = true)

        {

            var query = AddDeletedFilter(Table, includeDeleted);

            query = func != null ? func(query) : query;

            return await query.ToPagedListAsync(pageIndex, pageSize, getOnlyTotalCount);

        }

        /// <param name="func">1个具有返回值的泛型异步委托方法实例,该泛型异步委托方法实例用于获取1个指定实体的1/n个实例。</param>

        /// <param name="pageIndex">当前页的页数值(“pageSize”结合,设定需要跳过指定实体实例的个数值),默认值:0,即不跳过。</param>

        /// <param name="pageSize">分页操作中每页最多显示实例的项()数值(“pageIndex”结合,设定需要跳过指定实体实例的个数值),默认值:int.MaxValue,即跳过int.MaxValue行数。 </param>

        /// <param name="getOnlyTotalCount">指示数据源中否包含有实例,默认值:false,该方法使用的是非操作,即数据源中否包含有实例,如果无实例,则(new)新实例化1个逻辑分页实例。</param>

        /// <param name="includeDeleted">指示是否包含指定实体中所有的逻辑删除项,默认值:true,即指定实体的所有实例。</param>

        /// <summary>

        /// 【异步分页列表】

        /// <remarks>

        /// 摘要:

        ///     以异步操作方式,只从数据源中加载指定1逻辑(内存)页中的数据,存储到“IPagedList(逻辑分页)”的实例中,从而尽量少的从指定表加载数据,从而尽量降低内存的消耗。

        ///  说明:

        ///     该方法没有定义缓存操作,更没有定义缓存的移除操作。

        /// </remarks>

        /// <returns>

        ///    “IPagedList(逻辑分页)”的实例,该实例存储着数据源中(符合指定查询条件的)所有实例。

        /// </returns>

        /// </summary>

        public virtual async Task<IPagedList<TEntity>> GetAllPagedAsync(Func<IQueryable<TEntity>, Task<IQueryable<TEntity>>> func = null,

            long pageIndex = 0, int pageSize = int.MaxValue, bool getOnlyTotalCount = false, bool includeDeleted = true)

        {

            var query = AddDeletedFilter(Table, includeDeleted);

            query = func != null ? await func(query) : query;

            return await query.ToPagedListAsync(pageIndex, pageSize, getOnlyTotalCount);

        }

对以上功能更为具体实现和注释见230525_016ShopRazor(逻辑分页实现)。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。