在表格的初始化定义实现中,有个最为重要的功能需要被定义实现,即分页,在通过Jquery DataTable插件分页渲染显示所有学生信息时,其中:分页功能的定义实现实际上是包含两部分:1、Jquery DataTable插件本身渲染分页定义实现;2、数据库学生表中所有学生实例的逻辑分页的实现定义实现。在实际上开发中为了简化实现学生实例的逻辑分页实际上是可以不被定义的,即可以不定义逻辑分页实现,而直接定义渲染分页实现;逻辑分页的功用是:逻辑分页只获取1个指定逻辑页面中的所有实例,由于被渲染数据量的减少,大大的提升了页面的刷新渲染速度。
1逻辑分页的定义实现
1 IPagedList
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <summary>
/// 【分页列表--接口】
/// <remarks>
/// 摘要:
/// 继承于该接口的具体实现类及其属性成员实例,为1个指定类的数据源执行分页操作提供数据支撑。
/// </remarks>
public interface IPagedList<T> : IList<T>
{
#region 属性
/// <summary>
/// 【页数索引】
/// <remarks>
/// 摘要:
/// 获取分页操作中当前页的页数值。
/// </remarks>
/// </summary>
int PageIndex { get; }
/// <summary>
/// 【页数大小】
/// <remarks>
/// 摘要:
/// 获取分页操作中每页最多显示实例的项(行)数值。
/// </remarks>
/// </summary>
int PageSize { get; }
/// <summary>
/// 【项数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中数据源实例的项(行)数总计值。
/// </remarks>
/// </summary>
int TotalCount { get; }
/// <summary>
/// 【页数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中页数总计值。
/// </remarks>
/// </summary>
int 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; }
#endregion
}
2 PagedList
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <summary>
/// 【分页列表--类】
/// <remarks>
/// 摘要:
/// 通过该类及其属性成员实例,为1个指定类的数据源执行分页操作提供数据支撑。
/// </remarks>
[Serializable]
public class PagedList<T> : List<T>, IPagedList<T>
{
#region 拷贝构造方法
/// <param name="source">以列表接口实例进行存储的1个指定类型的数据源(包含:数组、列表、字典等)。</param>
/// <param name="pageIndex">分页操作中当前页的页数值。</param>
/// <param name="pageSize">分页操作中每页最多显示实例的项(行)数值。 </param>
/// <param name="totalCount">分页操作中实例的项(行)数总计值。</param>
/// <summary>
/// 【拷贝构造方法】
/// <remarks>
/// 摘要:
/// 通过拷贝构造方法为该类中的属性成员赋值实例化初始值。
/// </remarks>
/// </summary
public PagedList(IList<T> source, int pageIndex, int pageSize, int? 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页数据,以最的小内存开销,为页面的渲染显示提供相应数据支撑,在当前类中基本没有起到应用的作用;真正起到应有作用的是定义在“PagedListExtension(AsyncIQueryableExtensions).ToPagedList”方法中的该方法。
AddRange(totalCount != null ? source : source.Skip(pageIndex * pageSize).Take(pageSize));
}
#endregion
#region 属性
/// <summary>
/// 【页数索引】
/// <remarks>
/// 摘要:
/// 获取分页操作中当前页的页数值。
/// </remarks>
/// </summary>
public int PageIndex { get; }
/// <summary>
/// 【页数大小】
/// <remarks>
/// 摘要:
/// 获取分页操作中每页最多显示实例的项(行)数值。
/// </remarks>
/// </summary>
public int PageSize { get; }
/// <summary>
/// 【项数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中数据源实例的项(行)数总计值。
/// </remarks>
/// </summary>
public int TotalCount { get; }
/// <summary>
/// 【页数总计】
/// <remarks>
/// 摘要:
/// 获取分页操作中页数总计值。
/// </remarks>
/// </summary>
public int 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
}
3 AsyncIQueryableExtensions
/// <summary>
/// 【异步语言集成查询扩展--类】
/// <remarks>
/// 摘要:
/// 为实现对“IQueryable”(语言集成查询,继承于“IEnumerable”)实例的异步操作,从而定义了该扩展类及其方法成员。
/// </remarks>
public static class AsyncIQueryableExtensions
{
/// <typeparam name="TSource">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="source">以“IQueryable”接口实例进行存储的1个指定类型的数据源(包含:可枚举类型(:“IEnumerable”,“IQueryable”继承于“IEnumerable”)、数组、列表、字典等)。</param>
/// <param name="predicate">以“lambda”表达式定义的查询条件实例,默认值:null,即获取数据源中所有实例的总计值。</param>
/// <summary>
/// 【异步总计】
/// <remarks>
/// 摘要:
/// 以异步操作方式,获取数据源中(符合指定查询条件的)所有实例的(整型)总计值。
/// </remarks>
/// <returns>
/// (整型)总计值。
/// </returns>
public static Task<int> CountAsync<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate = null)
{
return predicate == null ? AsyncExtensions.CountAsync(source) : AsyncExtensions.CountAsync(source, predicate);
}
/// <typeparam name="TSource">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="source">以“IQueryable”接口实例进行存储的1个指定类型的数据源(包含:可枚举类型(:“IEnumerable”,“IQueryable”继承于“IEnumerable”)、数组、列表、字典等)。</param>
/// <summary>
/// 【异步列表】
/// <remarks>
/// 摘要:
/// 以异步操作方式,把数据源中(符合指定查询条件的)所有实例的存储到列表实例中。
/// </remarks>
/// <returns>
/// 列表实例。
/// </returns>
public static Task<List<TSource>> ToListAsync<TSource>(this IQueryable<TSource> source)
{
return AsyncExtensions.ToListAsync(source);
}
/// <typeparam name="TSource">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="source">以“IQueryable”接口实例进行存储的1个指定类型的数据源(包含:可枚举类型(:“IEnumerable”,“IQueryable”继承于“IEnumerable”)、数组、列表、字典等)。</param>
/// <param name="pageIndex">当前页的页数值(与“pageSize”结合,设定需要跳过指定实体实例的个数值)。</param>
/// <param name="pageSize">分页操作中每页最多显示实例的项(行)数值(与“pageIndex”结合,设定需要跳过指定实体实例的个数值)。 </param>
/// <param name="getOnlyTotalCount">指示是否只从数据源中加载指定1页中的数据,以最少的数据量,提升页面的刷新渲染速度,默认值:false,即只从数据源中加载指定1页中的数据</param>
/// <summary>
/// 【异步分页列表】
/// <remarks>
/// 摘要:
/// 以异步操作方式,只从数据源中加载指定1页中的数据,存储到“PagedList”类的实例中,为页面的渲染显示提供相应数据支撑。
/// </remarks>
/// <returns>
/// 分页列表实例。
/// </returns>
public static async Task<IPagedList<TSource>> ToPagedListAsync<TSource>(this IQueryable<TSource> source, int pageIndex, int pageSize, bool getOnlyTotalCount = false)
{
if (source == null)
return new PagedList<TSource>(new List<TSource>(), pageIndex, pageSize);
//分页操作中每页最多显示实例的项(行)数值,“Math.Max”方法保证该参数实例的最小值必须>=1。
pageSize = Math.Max(pageSize, 1);
var count = await source.CountAsync();
var data = new List<TSource>();
//指示只从数据源获取指定1页中的数据,从而以最少的数据量,提升页面的刷新渲染速度。
if (!getOnlyTotalCount)
data.AddRange(await source.Skip(pageIndex * pageSize).Take(pageSize).ToListAsync());
//把数据存储到分页列表实例中,为页面的渲染显示提供相应数据支撑。
return new PagedList<TSource>(data, pageIndex, pageSize, count);
}
}
4 SearchStudentPagedListAsync
/// <param name="pageIndex">当前页,默认值:0,即不跳过学生实体的任何实例。</param>
/// <param name="pageSize">每1页中最多的实例数,默认值:int.MaxValue,即每1面中最多包含:2147483647条实例。</param>
/// <param name="code">1个指定的学号字符串,默认值:null,即获取学生实体的所有实例。</param>
/// <param name="name">学生姓名(支持模糊查询),默认值:null,即获取学生实体的所有实例。</param>
/// <param name="specialtyId">专业实体的1个指定整型编号值,默认值:0,即获取学生实体的所有实例。</param>
/// <param name="gradeId">年级实体的1个指定整型编号值,默认值:0,即获取学生实体的所有实例。</param>
/// <param name="categoryId">班级实体的1个指定整型编号值,默认值:0,即获取学生实体的所有实例。</param>
/// <summary>
/// 【异步学生分页列表】
/// <remarks>
/// 摘要:
/// 以异步操作方式,把学生表中符合查询条件的所有实例存储到分页列表实例中,为页面的渲染显示提供相应数据支撑。
/// </remarks>
/// <returns>
/// 分页列表实例。
/// </returns>
protected virtual async Task<IPagedList<Student>> SearchStudentPagedListAsync(int pageIndex = 0, int pageSize = int.MaxValue,
string code = null, string name = null, int specialtyId = 0, int gradeId = 0, int categoryId = 0)
{
//如果数据库不支持int.MaxValue,则即每1面中最多包含的实例数为:int.MaxValue - 1。
if (pageSize == int.MaxValue)
pageSize = int.MaxValue - 1;
var _studentsIQueryable = from studentsQuery in _context.StudentDbSet.Include(student => student.SpecialtySingleton).Include(student => student.GradeSingleton).Include(student => student.CategorySingleton) select studentsQuery;
if (!string.IsNullOrEmpty(name))
_studentsIQueryable = _studentsIQueryable.Where(s => s.Name.Contains(name));
if (!string.IsNullOrEmpty(code) || specialtyId > 0 || gradeId > 0 || categoryId > 0)
_studentsIQueryable = _studentsIQueryable.Where(s => s.Code == code || s.SpecialtyId == specialtyId || s.GradeId == gradeId || s.CategoryId == categoryId);
return await _studentsIQueryable.OrderByDescending(s => s.Id).ToPagedListAsync(pageIndex, pageSize);
}
2 渲染分页的定义实现
1 StudentModel
public partial record StudentModel
{
#region 属性
/// <summary>
/// 【编号】
/// <remarks>
/// 摘要:
/// 获取/设置学生实体1个指定实例的整型编号值。
/// </remarks>
/// </summary>
public int Id { get; set; }
/// <summary>
/// 【学号】
/// <remarks>
/// 摘要:
/// 获取/设置学生实体1个指定实例的学号。
/// </remarks>
/// </summary>
public string Code { get; set; }
/// <summary>
/// 【姓名】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定学生的姓名。
/// </remarks>
/// </summary>
public string Name { get; set; }
/// <summary>
/// 【专业编号】
/// <remarks>
/// 摘要:
/// 获取/设置专业实体1个指定实例的整型编号值。
/// </remarks>
/// </summary>
public string SpecialtyName { get; set; }
/// <summary>
/// 【年级编号】
/// <remarks>
/// 摘要:
/// 获取/设置年级实体1个指定实例的整型编号值。
/// </remarks>
/// </summary>
public string GradeName { get; set; }
/// <summary>
/// 【班级编号】
/// <remarks>
/// 摘要:
/// 获取/设置班级实体1个指定实例的整型编号值。
/// </remarks>
/// </summary>
public string CategoryName { get; set; }
#endregion
}
2 BasePagedListModel
namespace JsonTable.Models.Paged
{
/*
IPagedModel<T>:
通过Jquery DataTabes插件实现分页操作,实际上是Jquery DataTabes插件分页与实体实例分页的结合与集成,nopCommerce开发者为了区分开Jquery DataTabes插件分页与实体实例逻辑分页,指定当前纪录定义继承于IPagedModel<T>接口,
以指定继承该纪录的指定模型纪录实例的分页操作是Jquery DataTabes插件分页;并对nopCommerce程序中通过IPagedList和PagedList所实现的实体实例逻辑分页操作作出明确的区分。
由于继承于BaseNopModel,基于相同的原因, 本人删除了当前纪录定义对IPagedModel<T>的继承,并不影响Jquery DataTabes插件的渲染显示。
*/
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <summary>
/// 【基本分页列表模型--纪录】
/// <remarks>
/// 摘要:
/// 该类及其属性成员实例,为Jquery DataTable插件渲染显示提供基本且必须的数据支撑。
/// 注意:
/// 该纪录是抽象纪录,所以该纪录只能被其它纪录所继承,并由继承纪录所实例化。
/// </remarks>
public abstract partial record BasePagedListModel<T>
{
#region 属性--Jquery DataTable插件
/// <summary>
/// 【数据】
/// <remarks>
/// 摘要:
/// 获取/设置以枚举数接口进行存储的1指定类中的所有实例。
/// 注意:
/// 因为Jquery DataTable插件是通过Jquery Ajax操作在浏览器中渲染出1指定类中的所有实例数据,所以该属性必须被命名为“Data”,也只能被命名为“Data”,否则浏览器中将不会渲染出这些数据。
/// </remarks>
/// </summary>
public IEnumerable<T> Data { get; set; }
/// <summary>
/// 【绘制】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定的Jquery DataTable插件被操作的次数值。
/// [JsonProperty(PropertyName = "string")]:
/// 如果对继承于该纪录的具体纪录实例在使用调用 Json(object)方法时,都会转到JsonConvert.SerializeObject(object)方法对具体纪录实例进行序列化编码,编码后的JSON数据中键(key)名,
/// 是通过[JsonProperty(PropertyName = "string")]标记所指定的键(key)名;在C#中如果不使用[JsonProperty(PropertyName = "string")]标记,则JSON数据中键(key)名与与属性成员名相同。
/// 但是在Jquery中JSON数据中键(key)名的第1个字母必须是小写字母,否则,将不会获取该键(key)名及其相对应值,为了让JSON数据同时兼容C#和Jquery,
/// 所以通过[JsonProperty(PropertyName = "string")]标记,把当前属性成员的第1个字母特定指定为小写字母,让Jquery能够获取JSON数据中键(key)名及其相对应值。
/// </remarks>
/// </summary>
[JsonProperty(PropertyName = "draw")]
public string Draw { get; set; }
/// <summary>
/// 【过滤总计】
/// <remarks>
/// 摘要:
/// 获取/设置符合指定过滤条件的1个指定实体所有实例的总计值。
/// [JsonProperty(PropertyName = "string")]:
/// 如果对继承于该纪录的具体纪录实例在使用调用 Json(object)方法时,都会转到JsonConvert.SerializeObject(object)方法对具体纪录实例进行序列化编码,编码后的JSON数据中键(key)名,
/// 是通过[JsonProperty(PropertyName = "string")]标记所指定的键(key)名;在C#中如果不使用[JsonProperty(PropertyName = "string")]标记,则JSON数据中键(key)名与与属性成员名相同。
/// 但是在Jquery中JSON数据中键(key)名的第1个字母必须是小写字母,否则,将不会获取该键(key)名及其相对应值,为了让JSON数据同时兼容C#和Jquery,
/// 所以通过[JsonProperty(PropertyName = "string")]标记,把当前属性成员的第1个字母特定指定为小写字母,让Jquery能够获取JSON数据中键(key)名及其相对应值。
/// </remarks>
/// </summary>
[JsonProperty(PropertyName = "recordsFiltered")]
public int RecordsFiltered { get; set; }
/// <summary>
/// 【总计】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定实体所有实例的总计值。
/// [JsonProperty(PropertyName = "string")]:
/// 如果对继承于该纪录的具体纪录实例在使用调用 Json(object)方法时,都会转到JsonConvert.SerializeObject(object)方法对具体纪录实例进行序列化编码,编码后的JSON数据中键(key)名,
/// 是通过[JsonProperty(PropertyName = "string")]标记所指定的键(key)名;在C#中如果不使用[JsonProperty(PropertyName = "string")]标记,则JSON数据中键(key)名与与属性成员名相同。
/// 但是在Jquery中JSON数据中键(key)名的第1个字母必须是小写字母,否则,将不会获取该键(key)名及其相对应值,为了让JSON数据同时兼容C#和Jquery,
/// 所以通过[JsonProperty(PropertyName = "string")]标记,把当前属性成员的第1个字母特定指定为小写字母,让Jquery能够获取JSON数据中键(key)名及其相对应值。
/// </remarks>
/// </summary>
[JsonProperty(PropertyName = "recordsTotal")]
public int RecordsTotal { get; set; }
#endregion
}
}
3 StudentPagedListModel
/// <summary>
/// 【学生分页列表模型--纪录】
/// <remarks>
/// 摘要:
/// 该纪录没有定义任何成员实现,它的作用只是能够通过该纪录能够对抽象基纪录进行实例化,为Jquery DataTable插件渲染显示提供基本且必须的数据支撑。
/// </remarks>
public partial record StudentPagedListModel : BasePagedListModel<StudentModel>
{
}
3 AsyncIEnumerableExtensions
public static class AsyncIEnumerableExtensions
{
/// <typeparam name="TSource">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <typeparam name="TResult">返回获取的,带有(阻塞性:ValueTask)异步操作状态信息的,泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="source">以“IEnumerable”(可枚举/枚举数)接口实例进行存储的1个指定类型的数据源(包含:可枚举类型(“IEnumerable”)、数组、列表、字典等)。</param>
/// <param name="predicate">1个通过(阻塞性:ValueTask)异步委托方法定义的查询条件实例。</param>
/// <summary>
/// 【异步总计】
/// <remarks>
/// 摘要:
/// 以(阻塞性:ValueTask)异步委托操作方式,获取数据源中符合指定查询条件的所有实例后,把这些实例存储到“IAsyncEnumerable”(可枚举/枚举数)接口实例中。
/// </remarks>
/// <returns>
/// “IAsyncEnumerable”(可枚举/枚举数)接口实例。
/// </returns>
public static IAsyncEnumerable<TResult> SelectAwait<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, ValueTask<TResult>> predicate)
{
return source.ToAsyncEnumerable().SelectAwait(predicate);
}
}
4 ModelExtensions
public static class ModelExtensions
{
/// <typeparam name="TListModel">泛型类型实例(这里特指:分页列表模型类型实例,例如:StudentPagedListModel)。</typeparam>
/// <typeparam name="TModel">泛型类型实例(这里特指:模型类型实例,例如:StudentModel)。</typeparam>
/// <typeparam name="TObject">泛型类型实例(这里特指:实体类型实例,例如:Student)。</typeparam>
/// <param name="listModel">分页列表模型的1个指定实例。</param>
/// <param name="searchModel">表单查询模型的1指定实例</param>
/// <param name="objectList">(逻辑)分页列表实例,只逻辑的从数据源中加载指定1页中的数据,存储到“PagedList”类的实例中,从而以最少的数据量,提升页面的刷新渲染速度。</param>
/// <param name="dataFillFunction">1个通过(阻塞性:ValueTask)异步委托方法定义的查询条件实例。</param>
/// <summary>
/// 【异步预处理表格】
/// <remarks>
/// 摘要:
/// 以异步操作方式,获取数据源中符合指定查询条件的所有实例后,把这些实例存储到“IAsyncEnumerable”(可枚举/枚举数)接口实例中。
/// </remarks>
/// <returns>
/// “IAsyncEnumerable”(可枚举/枚举数)接口实例。
/// </returns>
public static async Task<TListModel> PrepareToGridAsync<TListModel, TModel, TObject>(this TListModel listModel,
BaseSearchModel searchModel, IPagedList<TObject> objectList, Func<IAsyncEnumerable<TModel>> dataFillFunction)
where TListModel : BasePagedListModel<TModel>
{
if (listModel == null)
throw new ArgumentNullException(nameof(listModel));
listModel.Data = await (dataFillFunction?.Invoke()).ToListAsync();
listModel.Draw = searchModel?.Draw;
listModel.RecordsTotal = objectList?.TotalCount ?? 0;
listModel.RecordsFiltered = objectList?.TotalCount ?? 0;
return listModel;
}
}
3 预处理与AJAX控制器
1 SearchStudentPagedListAsync、PrepareStudentPagedListModelAsync
/// <param name="pageIndex">当前页,默认值:0,即不跳过学生实体的任何实例。</param>
/// <param name="pageSize">每1页中最多的实例数,默认值:int.MaxValue,即每1面中最多包含:2147483647条实例。</param>
/// <param name="code">1个指定的学号字符串,默认值:null,即获取学生实体的所有实例。</param>
/// <param name="name">学生姓名(支持模糊查询),默认值:null,即获取学生实体的所有实例。</param>
/// <param name="specialtyId">专业实体的1个指定整型编号值,默认值:0,即获取学生实体的所有实例。</param>
/// <param name="gradeId">年级实体的1个指定整型编号值,默认值:0,即获取学生实体的所有实例。</param>
/// <param name="categoryId">班级实体的1个指定整型编号值,默认值:0,即获取学生实体的所有实例。</param>
/// <summary>
/// 【异步学生(逻辑)分页列表】
/// <remarks>
/// 摘要:
/// 以异步操作方式,把学生表中符合查询条件的所有实例存储到(逻辑)分页列表实例中,为(渲染)分页列表的实例化提供相应数据支撑。
/// </remarks>
/// <returns>
/// (逻辑)分页列表实例。
/// </returns>
protected virtual async Task<IPagedList<Student>> SearchStudentPagedListAsync(int pageIndex = 0, int pageSize = int.MaxValue,
string code = null, string name = null, int specialtyId = 0, int gradeId = 0, int categoryId = 0)
{
//如果数据库不支持int.MaxValue,则即每1面中最多包含的实例数为:int.MaxValue - 1。
if (pageSize == int.MaxValue)
pageSize = int.MaxValue - 1;
var _studentsIQueryable = from studentsQuery in _context.StudentDbSet.Include(student => student.SpecialtySingleton).Include(student => student.GradeSingleton).Include(student => student.CategorySingleton) select studentsQuery;
if (!string.IsNullOrEmpty(name))
_studentsIQueryable = _studentsIQueryable.Where(s => s.Name.Contains(name));
if (!string.IsNullOrEmpty(code) || specialtyId > 0 || gradeId > 0 || categoryId > 0)
_studentsIQueryable = _studentsIQueryable.Where(s => s.Code == code || s.SpecialtyId == specialtyId || s.GradeId == gradeId || s.CategoryId == categoryId);
return await _studentsIQueryable.OrderByDescending(s => s.Id).ToPagedListAsync(pageIndex, pageSize);
}
/// <param name="studentSearchModel">学生查询模型纪录的1个指定实例。</param>
/// <summary>
/// 【异步学生(渲染)分页列表】
/// <remarks>
/// 摘要:
/// 以异步操作方式,把学生表中符合查询条件的所有实例存储到(渲染)分页列表实例中,为页面的渲染显示提供相应数据支撑。
/// </remarks>
/// <returns>
/// (逻辑)分页列表实例。
/// </returns>
public virtual async Task<StudentPagedListModel> PrepareStudentPagedListModelAsync(StudentSearchModel studentSearchModel)
{
IPagedList<Student> _studentIPagedList = await SearchStudentPagedListAsync(studentSearchModel.Page - 1,
studentSearchModel.PageSize,
studentSearchModel.Code,
studentSearchModel.Name,
studentSearchModel.SpecialtyId,
studentSearchModel.GradeId,
studentSearchModel.CategoryId);
var model = await new StudentPagedListModel().PrepareToGridAsync(studentSearchModel, _studentIPagedList, () =>
{
//由于当前返回的是一个1个(阻塞性:ValueTask)异步委托方法定义的查询条件实例,在该方法中并设有调用相关的异步方法所以“lambda”表达式会出语法现警告。
return _studentIPagedList.SelectAwait(async item =>
{
StudentModel _studentModel = new StudentModel();
_studentModel.Id = item.Id;
_studentModel.Name = item.Name;
_studentModel.Code = item.Code;
_studentModel.SpecialtyName = item.SpecialtySingleton.Name;
_studentModel.GradeName = item.GradeSingleton.Name;
_studentModel.CategoryName = item.CategorySingleton.Name;
return _studentModel;
});
});
return model;
}
2 StudentListAjax
/// <param name="studentSearchModel">学生查询模型纪录的1个指定实例。</param>
/// <summary>
/// 【学生表格局部刷新】
/// <remarks>
/// 摘要:
/// 通过参数实例,实现学生表格的局部刷新渲染。
/// </remarks>
/// </summary>
/// <returns>
/// 学生表格局部刷新渲染所需要的JSON格式的数据。
/// </returns>
[HttpPost]
public virtual async Task<IActionResult> StudentListAjax(StudentSearchModel studentSearchModel)
{
var model = await PrepareStudentPagedListModelAsync(studentSearchModel);
return Json(model);
}
4 渲染流程和改进
nopCommerce程序中对Jquery DataTable插件的渲染的定义实现是从小模型纪录实例向大模型纪录实例进行集成,或通过小模型纪录实例对大模型记录实例进行实例化,但这也造成了BaseSearchModel定义与 BasePagedListModel定义中有重复的与Jquery DataTable插件相关的属性成员,同时nopCommerce程序中还没有定义与排序相关的模型纪录,如果定义,按照nopCommerce程序所给定的渲染流程,则排序相关的模型纪录定义中也将会定义出一些重复的与Jquery DataTable插件相关的属性成员。为了简化渲染的定义实现,必须对原有的渲染流程进行如下图所示:
注意:
该示例程序中的所有渲染实现都通过Jquery Ajax方法 触发指定的URL,根据该URL调用指定的控件器方法,控件器方法经过一系列执行操作后,向Jquery Ajax方法发送JSON格式的渲染数据,最后通过Jquery语言调用JSON格式的渲染数据,把班级下拉控件和学生表格进行局部渲染显示,但是种方式中需要使用大量的Jquery语言,由Jquery语言是非强类型的,所以总是对程序的健壮性有一些若有若无的担心。解决这种焦虑的最好方法就是根据第二种渲染流程,通过指定的控件器方法向cshtml文件中发送model强类型实例结合HTML标签或以cshtml局部页定义的部件,来实现对班级下拉控件和学生表格进行局部渲染显示;在该方式中Jquery Ajax方法只用于触发指定的URL,调用指定的控件器方法,而渲染的定义实现则交由了model强类型实例和合HTML标签或cshtml局部页。以上这些改进只是一种合理的想法,并没有相应的示例程序作为支撑,如果和时间我会按照该想法来实现相应的示例程序的。
对以上功能更为具体实现和注释见:22-09-08-068_JsonTable(完整定义nopCommerce Jquery DataTable插件之初始化学生表格)。