第7章 分页之模型分页(物理分页)

Source

    模型分页主要通过前端页面中的分页控件,向后端指定路由(控制器行为方法),传递相等的参数,然后间接的通过后端程序从数据库指定表中获取一些指定行的数据,然后把这些数据在前端页面渲染显示出来。模型分页的主要用于前端页面与后端程序之间的直接的数据交互操作,并且模型分页的实例数据最终需要在前端页面上进渲染显示的,因此又称模型分页为:物理分页,与之相对应的是:逻辑分页,这将在下面的章节中进行讲解。

    不管理是“物理分页”还是“逻辑分页”,二者都是,只把当前页面渲染显示所必须的数据,通过当前程序间接的从数据库的指定表中取出并存储到内存中,这种操作的好处是尽量减少资源开锁,特别是内存的开销。

1 准备工作

1.1 Data.Extensions.ParameterRebinder

using System.Linq.Expressions;

namespace Data.Extensions

{

    /// <summary>

    /// 【参数重新绑定--类】

    /// <remarks>

    /// 摘要:

    ///     通过该类中的方法成员把1个指定的“lambda”表达式,中的指定参数替换(这里主要指“lambda”表达式中指针实例的替换)后,返回1个新的被替换后的“lambda”表达式。

    /// </remarks>

    /// </summary>

    public class ParameterRebinder : ExpressionVisitor

    {

        #region 拷贝构造方法与变量

        /// <summary>

        /// 【映射】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个指定的典实例,该实例中存储着“lambda”表达式替换的需要的参数实例。

        /// </remarks>

        /// </summary>

        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        ///<param name="map">字典实例,该实例中存储着“lambda”表达式替换的需要的参数实例。</param>

        /// <summary>

        /// 【拷贝构建方法】

        /// <remarks>

        /// 摘要:

        ///     通过拷贝构建方法,实例化该类中的字典成员变量。

        /// </remarks>

        /// </summary>

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)

        {

            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();

        }

        #endregion

        #region 方法--私有/保护--覆写

        ///<param name="p">1个指定“lambda”表达式的指针实例。</param>

        /// <summary>

        /// 【参数访问】

        /// <remarks>

        /// 摘要:

        ///     通过该方法将调用其基类同名方法,把1个指定的“lambda”表达式,中的指定参数替换后,返回1个新的被替换后的“lambda”表达式。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个新的被替换后的“lambda”表达式。

        /// </returns>

        /// </summary>

        protected override Expression VisitParameter(ParameterExpression p)

        {

            ParameterExpression replacement;

            //如果字典实例包含指针实例,则抛出指针实例,并使用抛出指针实例替换原“lambda”表达式中的指针实例。

            if (map.TryGetValue(p, out replacement!)) p = replacement;

            //调用其基类同名方法,获取1个新的被替换后的“lambda”表达式。

            return base.VisitParameter(p);

        }

        #endregion

        #region 方法

        ///<param name="map">字典实例,该实例中存储着“lambda”表达式替换的需要的参数实例。</param>

        ///<param name="exp">1个将被替换参数的“lambda”表达式。</param>

        /// <summary>

        /// 【参数替换】

        /// <remarks>

        /// 摘要:

        ///     通过该方法把1个指定的“lambda”表达式,中的指定参数替换后,返回1个新的被替换后的“lambda”表达式。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个新的被替换后的“lambda”表达式。

        /// </returns>

        /// </summary>

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)

        {

            return new ParameterRebinder(map).Visit(exp);

        }

        #endregion

    }

}

1.2 Data.Extensions.ExpressionExtension

using System.Linq.Expressions;

namespace Data.Extensions

{

    /// <summary>

    /// “lambda”表达式扩展--类】

    /// <remarks>

    /// 摘要:

    ///     通过该类中的方法成员获取1个指定实例的“lambda”表达式实例,或组合出该指定实例的1个新的“lambda”表达式实例。

    /// </remarks>

    /// </summary>

    public static class ExpressionExtension

    {

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

        /// <summary>

        /// 【真】

        /// <remarks>

        /// 摘要:

        ///     该方法以泛型形式获取1个指定实例的非空“lambda”表达式实例。

        /// 说明:

        ///     1、这种方式是以泛型形式获取1个指定实例的非空“lambda”表达式实例的最简实现。

        ///     21个指定实例以这种方式执行“lambda”操作后,该实例中包含的所有子实例都为:true,即不执行筛选过滤操作。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定实例的非空“lambda”表达式实例。

        /// </returns>

        /// </summary>

        public static Expression<Func<T, bool>> True<T>()

        {

            return f => true;

        }

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

        /// <summary>

        /// 【假】

        /// <remarks>

        /// 摘要:

        ///     该方法以泛型形式获取1个指定实例的非空“lambda”表达式实例。

        /// 说明:

        ///     1、这种方式是以泛型形式获取1个指定实例的非空“lambda”表达式实例的最简实现。

        ///     21个指定实例以这种方式执行“lambda”操作后,该实例中所包含的所有子实例都为:false,即该实例中不包含的任何的子实例,或者说子实例都被筛选过滤掉了。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定实例的非空“lambda”表达式实例。

        /// </returns>

        /// </summary>

        public static Expression<Func<T, bool>> False<T>()

        {

            return f => false;

        }

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

        ///<param name="first">1“lambda”表达式实例。</param>

        ///<param name="second">2“lambda”表达式实例。</param>

        ///<param name="merge">1个将被替换参数的“lambda”表达式。</param>

        /// <summary>

        /// 【组合】

        /// <remarks>

        /// 摘要:

        ///     该方法是以泛型形式把第1“lambda”表达式指针与第1“lambda”表达式中的主体和第2“lambda”表达式中的主体,共同组合出完整的新的“lambda”表达式。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个同组合出完整的新的“lambda”表达式。

        /// </returns>

        /// </summary>

        public static Expression<T> Compose<T>(this Expression<T> first,

            Expression<T> second,

            Func<Expression, Expression, Expression> merge)

        {

            //获取把第2“lambda”表达式指针及其指针实例,并存储到到字典实例中。

            var map = first.Parameters//1“lambda”表达式指针

                .Select((f, i) => new { f, s = second.Parameters[i] })//second.Parameters[i]:2“lambda”表达式指针。第1“lambda”表达式指针被第2“lambda”表达式指针所替换。

                .ToDictionary(p => p.s, p => p.f);//把第2“lambda”表达式指针及其指针实例,存储到字典实例中。

            //通过典实例中的数据,对第2“lambda”表达式中的主体中的指针替换后,获取1个新的第2“lambda”表达式中的主体

            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // 把第1“lambda”表达式指针与第1“lambda”表达式中的主体和第2“lambda”表达式中的主体,共同组合出完整的新的“lambda”表达式。

            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);

        }

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

        ///<param name="first">1“lambda”表达式实例。</param>

        ///<param name="second">2“lambda”表达式实例。</param>

        /// <summary>

        /// 【逻辑与操作】

        /// <remarks>

        /// 摘要:

        ///     该方法是以泛型形式把第1“lambda”表达式指针与第1“lambda”表达式中的主体和第2“lambda”表达式中的主体,以逻辑与操作方式,共同组合出完整的新的“lambda”表达式。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     以逻辑与操作方式,共同组合出完整的新的“lambda”表达式。

        /// </returns>

        /// </summary>

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first,

            Expression<Func<T, bool>> second)

        {

            return first.Compose(second, Expression.And);

        }

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

        ///<param name="first">1“lambda”表达式实例。</param>

        ///<param name="second">2“lambda”表达式实例。</param>

        /// <summary>

        /// 【逻辑或操作】

        /// <remarks>

        /// 摘要:

        ///    该方法是以泛型形式把第1“lambda”表达式指针与第1“lambda”表达式中的主体和第2“lambda”表达式中的主体,以逻辑或操作方式,共同组合出完整的新的“lambda”表达式。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     以逻辑或操作方式,共同组合出完整的新的“lambda”表达式。

        /// </returns>

        /// </summary>

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first,

            Expression<Func<T, bool>> second)

        {

            return first.Compose(second, Expression.Or);

        }

    }

}

1.3 Data.Extensions.QueryableExtension

using System.Linq.Expressions;

namespace Data.Extensions

{

    /// <summary>

    /// Queryable扩展类--类】

    /// <remarks>

    /// 摘要:

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

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

    /// </remarks>

    /// </summary>

    public static class QueryableExtension

    {

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

        ///<param name="source">以枚举数接口实例进行存储的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">以枚举数接口实例进行存储的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">以枚举数接口实例进行存储的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);

        }

    }

}

1.4 Data.Extensions.EnumerableExtension

namespace Data.Extensions

{

    /// <summary>

    /// 【可枚举扩展类--类】

    /// <remarks>

    /// 摘要:

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

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

    /// </remarks>

    /// </summary>

    public static class EnumerableExtension

    {

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

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

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

        /// <summary>

        /// 【长整型跳过】

        /// <remarks>

        /// 摘要:

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

        /// </remarks>

        /// <returns>

        /// 返回:

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

        /// </returns>

        /// </summary>

        public static IEnumerable<TSource> LongSkip<TSource>(this IEnumerable<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;

        }

    }

}

2 物理分页(模型分页)

2.1 WebApi.Models.PageModel<T>

namespace WebApi.Models

{

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

    /// <summary>

    /// 【页模型--纪录】

    /// </summary>

    /// <remarks>

    /// 摘要:

    ///     通过该纪录的属性成员对1指定逻辑(内存)页面内的指定实体/模型的所有实例进行存储,这些实例数据为页面的渲染显示提供数据支撑。

    ///  说明:

    ///     该模型纪录的实例实质上也是逻辑(内存)页面,但是该实例直接为页面的渲染显示提供数据支撑,所以把该模型类的实例称之为:物理页。

    /// </remarks>

    public record PageModel<T>

    {

        /// <summary>

        /// 【当前页】

        /// <remarks>

        /// 摘要:

        ///     获取/设置当前页,即从前端页面所传递过来的当前页面的长整型索引值,默认值:1,即当前前端页面所渲染显示的是第1页。

        /// </remarks>

        /// </summary>

        public long PageIndex { get; set; } = 1;

        /// <summary>

        /// 【页面大小】

        /// <remarks>

        /// 摘要:

        ///     获取/设置页面大小,即前端页面内最多所渲染显示的实例项的长型个数值,默认值:10,即页面内最多渲染显示10项实例。

        /// </remarks>

        /// </summary>

        public int PageSize { set; get; } = 10;

        /// <summary>

        /// 【总计值】

        /// <remarks>

        /// 摘要:

        ///     获取/设置符合指定条件的实例项的长整型总计值,即前端页面中所有页面所渲染显示的实例项的长型个数值,默认值:0,即还没有任何的实例数据需要前端页面进行渲染显示。

        /// </remarks>

        /// </summary>

        public long TotalCount { get; set; } = 0;

        /// <summary>

        /// 【总页数】

        /// <remarks>

        /// 摘要:

        ///     获取/设置当前页,即从指定表中获取当前逻辑页面内的所有实例,可以分为页数的总计值。

        /// 说明:

        ///     “Math.Ceiling”方法:向上进位取整,即如果除不尽,那么就向上进1,例如:Math.Ceiling(2.1) = 3;与之相对应的是Math.Floor方法:向下舍位取整。例:Math.Floor(2.9)=2

        /// </remarks>

        /// </summary>

        public long TotalPage => (long)Math.Ceiling((decimal)TotalCount / PageSize);

        /// <summary>

        /// 【数据】

        /// <remarks>

        /// 摘要:

        ///     获取/设置可枚举实例,把1指定逻辑页面内的指定实体/模型的所有实例存储到该可枚举实例中,从而为前端当页面的渲染显示提供数据支撑。

        /// </remarks>

        /// </summary>

        public IEnumerable<T> Data { get; set; }

    }

}

2.2 WebApi.Models.MessageModel<T>

namespace WebApi.Models

{

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

    /// <summary>

    /// 【消息模型--纪录】

    /// </summary>

    /// <remarks>

    /// 摘要:

    ///     通过该纪录的属性成员对指定“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。

    /// </remarks>

    public record MessageModel<T>

    {

        /// <summary>

        /// 【状态码】

        /// <remarks>

        /// 摘要:

        ///     获取/设置指定控制器行方法的执行结果状态码的整型值,默认值:200(执行成功的状态码)。

        /// </remarks>

        /// </summary>

        public int Status { get; set; } = 200;

        /// <summary>

        /// 【成功?】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个值false(默认值,执行失败)/true(执行成功),该值指示指定“Api”控制器行方法的执行操作是否处于成功状态。

        /// </remarks>

        /// </summary>

        public bool Success { get; set; } = false;

        /// <summary>

        /// 【信息】

        /// <remarks>

        /// 摘要:

        ///     获取/设置指定“Api”控制器行方法的执行状态所对应的解释性信息,默认值:"",即执行状态没有所对应的解释性信息。

        /// </remarks>

        /// </summary>

        public string Message { get; set; } = "";

        /// <summary>

        /// 【泛型应答实例】

        /// <remarks>

        /// 摘要:

        ///     获取/设置泛型实例,该实例泛型用于存储指定实体/模型类的所有实例,为客户端页面的渲染提供数据支撑。

        /// </remarks>

        /// </summary>

        public T Response { get; set; }

    }

}

2.3 WebApi.Models.PaginationModel

namespace WebApi.Models

{

    /// <summary>

    /// 【分页模型--纪录】

    /// </summary>

    /// <remarks>

    /// 摘要:

    ///     通过该纪录的属性成员对前端页面RUL路由或表单中所传递的分页参数进行存储,以实现页面对符合条件的数据进行渲染显示。

    /// </remarks>

    public record PaginationModel

    {

        /// <summary>

        /// 【当前页】

        /// <remarks>

        /// 摘要:

        ///     获取/设置当前页,即从前端页面所传递过来的当前页面的长整型索引值,默认值:1,即当前前端页面所渲染显示的是第1页。

        /// </remarks>

        /// </summary>

        public long PageIndex { get; set; } = 1;

        /// <summary>

        /// 【页面大小】

        /// <remarks>

        /// 摘要:

        ///     获取/设置页面大小,即前端页面内最多所渲染显示的实例项的长型个数值,默认值:10,即页面内最多渲染显示10项实例。

        /// </remarks>

        /// </summary>

        public int PageSize { get; set; } = 10;

        /// <summary>

        /// 【排序字段】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个指定实例的排序字段及其排序方式,例如:Filed="Id"&&Type="desc""{\"Filed\":\"id\",\"Type\":\"desc\"}"

        /// </remarks>

        /// </summary>

        public string OrderByFiled { get; set; }

        /// <summary>

        /// 【筛选条件集】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个指定实例的筛选条件,例如:Name=""&&IsActive="true""{\"Name\":\"\",\"IsActive\":true}"

        /// </remarks>

        /// </summary>

        public string QueryCondition  { get; set; }

    }

}

3 WebApi.Controllers.RoleController.GetRolePageByFromBodyAsync

   /// <summary>

        /// 【表单单击提交分页--需权限】

        /// </summary>

        /// <remarks>

        /// 摘要:

        ///     通过表单所传递的参数实例,获取符合条件的1指定页面内的所有数据,为指定页面的渲染显示提供数据支撑。

        /// 注意:

        ///     表单提交分页必须使用“[HttpPost]”进行标记。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     消息模型纪录的1个指定实例,该实例存储当前“Api”方法的执行操作结果,为客户端页面的渲染提供数据支撑。

        /// </returns>

        [HttpPost]

        public async Task<MessageModel<PageModel<Role>>> GetRolePageByFromBodyAsync([FromBody] PaginationModel pagination)

        {

            //根据筛选字符串,为角色实例组合筛选lambda”表达式实例。

            Expression<Func<Role, bool>> _expression = null;

            if (!string.IsNullOrEmpty(pagination.QueryCondition))

            {

                _expression = ExpressionExtension.True<Role>();

                Role _role = JsonConvert.DeserializeAnonymousType(pagination.QueryCondition, new Role());

                if (_role!.Id > 0)

                {

                    _expression = _expression.And(p => p.Id == _role!.Id);

                }

                if (!string.IsNullOrEmpty(_role!.Name))

                {

                    _expression = _expression.And(p => p.Name.Contains(_role!.Name));

                }

                if (pagination.QueryCondition.Contains("IsActive", StringComparison.InvariantCultureIgnoreCase))

                {

                    _expression = _expression.And(p => p.IsActive == _role!.IsActive);

                }

                if (!string.IsNullOrEmpty(_role!.Remark))

                {

                    _expression = _expression.And(p => p.Remark.Contains(_role!.Remark));

                }

            }

            IQueryable<Role> _roleQueryable = _context.GetDbSet<Role>().AsQueryable();

            //获取所有符合条件的角色实例。

            if (_expression != null)

            {

                _roleQueryable = _roleQueryable.Where(_expression);

            }

            //把所有符合条件的角色实例,按照指定字段进行排序操作。

            if (!string.IsNullOrEmpty(pagination.OrderByFiled))

            {

                var _obj = JsonConvert.DeserializeAnonymousType(pagination.OrderByFiled, new

                {

                    Filed = "",

                    Type = "",

                });

                if (_obj!.Type.Equals("asc", StringComparison.InvariantCultureIgnoreCase))

                {

                    _roleQueryable = _roleQueryable.OrderBy(_obj!.Filed);

                }

                if (_obj!.Type.Equals("desc", StringComparison.InvariantCultureIgnoreCase))

                {

                    _roleQueryable = _roleQueryable.OrderByDescending(_obj!.Filed);

                }

            }

            else

            {

                _roleQueryable = _roleQueryable.OrderBy(r => r.Id);

            }

            //根据前端页面传递的参数,从角色表中获取(1逻辑页中的)相应行数的数据,并把这些数据存储到列表实例中。

            List<Role> _roleSinglePageList = await _roleQueryable

                 .LongSkip((pagination.PageIndex - 1) * pagination.PageSize)

                 .Take(pagination.PageSize)

                 .ToListAsync();

            //实例化当前模型页(“物理页”),为当前页面的渲染显示提供数据支撑。

            PageModel<Role> _rolePageModel = new PageModel<Role>()

            {

                PageIndex = pagination.PageIndex,

                PageSize = pagination.PageSize,

                TotalCount = _roleQueryable.LongCount(),

                Data = _roleSinglePageList,

            };

            //实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。

            MessageModel<PageModel<Role>> _roleMessageModel = new MessageModel<PageModel<Role>>()

            {

                Success = true,

                Message = "获取成功",

                Response = _rolePageModel,

            };

            return _roleMessageModel;

        }

4 调试

 对以上功能更为具体实现和注释见221228_004ShopDemo(分页之模型分页(物理分页))。