1 Score(成绩)实体重构为Exam(考试)实体
/// <summary>
/// 【考试--类】
/// <remarks>
/// 摘要:
///     通过该实体类及其属性成员,用于实现当前程序【Data】.【领域】.【学生集】.【考试】实体与“[SqlSugarAcquaintance].[Exam]”表之间的CURD的交互操作,并把这些数据存储到数据库设置实例中(内存)。
/// </remarks>
/// </summary>
public class Exam
    {
/// <summary>
/// 【编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置考试实体1个指定实例的整型编号值。
/// [SugarColumn(IsIdentity = true, IsPrimaryKey = true)]标记:
///     该标记将该属性成员所映射[Exam].[Id]字段约束定义主键。
/// </remarks>
        [SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
public int Id { get; set; }
/// <summary>
/// 【学生编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置班级实体1个指定实例的整型编号值。
/// 注意:
///     “SqlSugarCore”中间件不支持外键/副键约束映射定义,即在Code-Frist模式下,该属性成员不能像“EFCore”那样被约束定义为:外键/副键
/// </remarks>
public int StudentId { get; set; }
/// <summary>
/// 【课程编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置课程实体1个指定实例的整型编号值。
/// 注意:
///     “SqlSugarCore”中间件不支持外键/副键约束映射定义,即在Code-Frist模式下,该属性成员不能像“EFCore”那样被约束定义为:外键/副键
/// </remarks>
public int CourseId { get; set; }
/// <summary>
/// 【名称】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置一次考试的名称。
///[SugarColumn(ColumnDataType = "nvarchar", Length =255)]标记:
///     该标记将该属性成员所映射[Course].[Name]字段的长度约束定义为:“255”,字段长度约束的默认类型为:“varchar,当前设定类型为;“nvarchar”
/// 同时标记的约束定义权限高于上下文约束定义的权限,即指定表中指定字段长度的约束类型和长度,最终将由该标记约束定义所决定。
/// </remarks>
        [SugarColumn(ColumnDataType = "nvarchar", Length = 255)]
public string Name { get; set; }
/// <summary>
/// 【成绩】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置1个指定考试实例中的成绩整型值。
/// </remarks>
        [SugarColumn(IsNullable = true)]
public int? Score { get; set; }
/// <summary>
/// 【备注】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置1个指定考试实例中的备注信息。
/// [SugarColumn(IsNullable = true, ColumnDataType = "nvarchar", Length = int.MaxValue)]标记:
///     该标记将该属性成员所映射[Course].[Comment]字段的长度约束定义为:“int.MaxValue”,字段长度约束的默认类型为:“varchar,当前设定类型为;“nvarchar”
/// 同时标记的约束定义权限高于上下文约束定义的权限,即指定表中指定字段长度的约束类型和长度,最终将由该标记约束定义所决定。
/// </remarks>
        [SugarColumn(IsNullable = true, ColumnDataType = "nvarchar", Length = int.MaxValue)]
public string Comment { get; set; }
/// <summary>
/// 【单个学生】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置专业实体的1个指定实例。
/// [Navigate(NavigateType.OneToOne, nameof(StudentId))]
///     该标记在当前程序使用级联查询操作时,直接获取学生实体的1个指定实例,如果该属性没有被该标记所标记则在使用级联查询操作时会出现异常。
/// </remarks>
        [SugarColumn(IsIgnore = true)]
        [Navigate(NavigateType.OneToOne, nameof(StudentId))]//1:1
public virtual Student StudentSingleton { get; set; }
/// <summary>
/// 【单个课程】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置年级实体的1个指定实例。
/// [Navigate(NavigateType.OneToOne, nameof(CourseId))]
///     该标记在当前程序使用级联查询操作时,直接获取课程实体的1个指定实例,如果该属性没有被该标记所标记则在使用级联查询操作时会出现异常。
/// </remarks>
        [SugarColumn(IsIgnore = true)]
        [Navigate(NavigateType.OneToOne, nameof(CourseId))]//1:1
public virtual Course CourseSingleton { get; set; }
    }
2 DataTable直接分页渲染显示
2.1 DirectRenderController.cs
public class DirectRenderController : Controller
    {
private readonly SqlSugarContext _context;
public DirectRenderController(SqlSugarContext context)
        {
            _context = context;
        }
public IActionResult Index()
        {
//通过级联操作,获取与考试相关的所有数据,为页面的渲染显示提供支撑。
            List<Exam> _examList = _context.SugarScope.Queryable<Exam>()
                .Includes(score => score.CourseSingleton)//1层级联
                .Includes(
                    score => score.StudentSingleton,
                    student => student.SpecialtySingleton)//2层级联
                .Includes(
                    score => score.StudentSingleton,
                    student => student.CategorySingleton)//2层级联
                 .Includes(
                    score => score.StudentSingleton,
                    student => student.CategorySingleton,
                    category => category.GradeSingleton)//3层级联
                .ToList();
return View(_examList);
        }
    }
2.2 \DirectRender\Index.cshtml
@using Data.Domain.Students
@model List<Exam>
@{
    ViewData["Title"] = "Index";
}
@section styles
{
<link rel="stylesheet" type="text/css" href="~/lib/DataTables-1.12.1/css/dataTables.bootstrap5.css">
}
<div class="row mt-3">
<div class="col-md-12">
<form asp-action="Index">
<div class="card">
<div class="card-body">
<h5 class="card-title"><i class="fas fa-list" aria-hidden="true"></i> 表格</h5>
<table id="example" class="display" style="width:100%">
<thead>
<tr style="width:100%">
<th>编号</th>
<th>学号</th>
<th>姓名</th>
<th>专业</th>
<th>年级</th>
<th>班级</th>
<th>考试名称</th>
<th>课程</th>
<th>成绩</th>
</tr>
</thead>
<tbody>
                        @foreach (var item in Model)
                        {
<tr>
<th>@item.Id</th>
<th>@item.StudentSingleton.Code</th>
<th>@item.StudentSingleton.Name</th>
<th>@item.StudentSingleton.SpecialtySingleton.Name</th>
<th>@item.StudentSingleton.CategorySingleton.GradeSingleton.Name</th>
<th>@item.StudentSingleton.CategorySingleton.Name</th>
<th>@item.Name</th>
<th>@item.CourseSingleton.Name</th>
<th>
                                    @if (item.Score == null)
                                    {
                                        @item.Comment
                                    }
else
                                    {
                                        @item.Score
                                    }
</th>
</tr>
                        }
</tbody>
</table>
</div>
</div>
</form>
</div>
</div>
   
@section Scripts 
{
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript" src="~/lib/DataTables-1.12.1/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="~/lib/DataTables-1.12.1/js/dataTables.bootstrap5.js"></script>
<script type="text/javascript">
        $(document).ready(function () {
            $('#example').DataTable({
                language: {
                    url: '../../lib/DataTables-1.12.1/localization/zh.json' //DataTables语言配置选项
                },
//样式配置
                bLengthChange: true,//是否在DataTableJquery插件中显示下拉框控件,true(默认值):显示。
                lengthMenu: [10, 20, 30, 50, 100], //DataTableJquery插件下拉框控件中的数量选择内容。
                searching: false, //是否在DataTableJquery插件中显示搜索框控件,false:不显示。
                autoWidth: true,//DataTableJquery插件全局自适应宽度,即插件中所有字段的宽度根据浏览器自动调整,true(默认值):自动调整。
                bInfo: true,//是否在DataTableJquery插件中显示是否显示信息 就是这个“第 1 至 10 项,共 x 项”信息,true(默认值):显示。
                pagingType: "numbers",//该样式类型禁止在DataTableJquery插件中,显示“上页”、“下页”等按钮。
//数据载入配置
//processing: true, //在ajax请求操作获取后端JSON编码格式的数据时,是否显示数据正处于加载...之中信息,true:显示。
//serverSide: true, //开启服务器模式,指示是否把ajax请求操作获取的数据加载到DataTableJquery插件的表格,true:加载。
//排序配置
                bSort: true,//是否启用DataTableJquery插件中的表头字段排序按钮,true(默认值):启用。
                order: [[0, 'desc']],//设置默认排序字段及其排序方式。
                columnDefs:
                [
                    { targets: [3, 4, 5, 6, 7], orderable: false }//设置DataTableJquery插件中禁止排序的字段。
                ],
            });
        });
</script>
}
3 SqlSugarCore中间件内置方法内存逻辑分页,之DataTable渲染显示
3.1 准备工作
3.1.1 SelectListItemExtension.cs(扩展)
/// <summary>
/// 【下拉框控件扩展】
/// </summary>
/// <remarks>
/// 摘要:
///     该类中的方法通过泛型操作,把1个指定类的所有实例,转换后存储到“List<SelectListItem>”实例中,为下拉框控件的渲染显示提供数据支撑。
/// </remarks>
public static class SelectListItemExtension
    {
///<typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
///<param name="list">列表实例,该实例存储着1个指定类的所有实例。</param>
///<param name="nameProperty">1个指定类中指定属性成员的名称,通过该属性成员的名称,获取该成员的实例,并把该实例赋值给“SelectListItem”的“Text”属性成员。</param>
///<param name="idProperty">1个指定类中指定属性成员的名称,通过该属性成员的名称,获取该成员的实例(该实例一般为指定类1个指定实例的整型编号值),并把该实例赋值给“SelectListItem”的“Value”属性成员。</param>
///<param name="isDefaultItem">指示是否把1个默认的“SelectListItem”实例,存储到“List<SelectListItem/>”实例中,默认值:true,即添加1个默认实例到“List<SelectListItem/>”实例中。</param>
///<param name="defaultItemText">1个默认的“SelectListItem”实例的“Text”属性成员所对应的实例值,默认值:null,即“Text”属性成员无对应的实例值。</param>
///<param name="defaultItemValue">1个默认的“SelectListItem”实例的“Value”属性成员所对应的实例值,默认值:0,即“Value”属性成员所对应的实例值为:0。</param>
/// <summary>
/// 【下拉列表扩展】
/// <remarks>
/// 摘要:
///   该方法通过泛型操作,把1个指定类的所有实例,转换后存储到“List<SelectListItem/>”实例中,为下拉框控件的渲染显示提供数据支撑。
/// </remarks>
/// </summary>
public static List<SelectListItem> AddSelectList<T>(this IList<T> list, string nameProperty, string idProperty, 
bool isDefaultItem = true, string defaultItemText = null, string defaultItemValue = "0")
        {
if (list == null)
throw new ArgumentNullException(nameof(list));
            List<SelectListItem> _selectList = new List<SelectListItem>();
            _selectList = list.Select(item => new SelectListItem
            {
                Text = item.GetType().GetProperty(nameProperty).GetValue(item).ToString(),
                Value = item.GetType().GetProperty(idProperty).GetValue(item).ToString(),
            }).ToList();
if(isDefaultItem)
            {
//1个指定下拉控件项所对应的默认文本值,该参数默认值:null(则默认文本值为:"全部")。
                defaultItemText ??= "全部";
//把1个指定下拉控件项实例,添加到1个指定的存储着1/多个下拉控件项的列表实例中。
                _selectList.Insert(0, 
new SelectListItem 
                    { 
                        Text = defaultItemText, 
                        Value = defaultItemValue,
                    });
            }
return _selectList;
        }
    }
3.1.2 BaseSearchModel.cs(抽象纪录)
/// <summary>
/// 【基本查询模型--纪录】
/// </summary>
/// <remarks>
/// 摘要:
///     把Jquery DataTables插件中当前1指定视图页的相关数据信息赋值给该纪录实例,为内存逻辑分页操作提供数据支撑。
/// 说明:
///     该记录被定义为抽象记录,就是为了被不同的指定查询模型记录所继承,如果不继承该抽象基记录,所有指定的查询模型记录,都需要重复性的对该抽象基记录中的所有属性成员进行定义。
/// </remarks>
public abstract record BaseSearchModel
    {
#region 属性--Jquery DataTables插件渲染显示基本数据支撑
/// <summary>
/// 【绘制】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置的Jquery DataTable插件在页面中被渲染(显示)出来的次数(当使用Ajax异步请求操作Jquery DataTable插件时,Jquery DataTable插件可能会不按照顺序返回后台action方法,
/// 因此必须通过该属性成员实例,来保证按照前台页面的操作顺序,调用后台action方法从而实现Jquery DataTable插件按照该顺序在页面中被渲染(显示)出来)。
/// 说明;
///     该属性成员实例只用于存储Jquery DataTables插件从前端传递过来的数据,内存逻辑分页操作无需该属性成员实例。
/// </remarks>
public string Draw { get; set; }
#endregion
#region 属性--内存逻辑分页基本数据支撑
/// <summary>
/// 【开始】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置内存逻辑分页操作,需要跳过的项数值:=(页数-1)*Length,Start>=0。  
/// </remarks>
public int Start { get; set; } = 0;
/// <summary>
/// 【页面大小】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置内存逻辑分页操作,每1页中存储指定实体实例的最多项数值,默认:10项指定实例,Length>=1。  
/// </remarks>
public int Length { get; set; } = 10;
#endregion
#region 属性--排序
/// <summary>
/// 【字段】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置内存逻辑分页操作时的排序字段。  
/// </remarks>
public string Column { get; set; }
/// <summary>
/// 【方式】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置内存逻辑分页操作时的排序方式:“asc/desc”。  
/// </remarks>
public string Dir { get; set; }
#endregion
    }
3.1.3 ExamSearchModel.cs(查询表单渲染,和为表格渲染提供支撑)
/// <summary>
/// 【考试查询模型--纪录】
/// </summary>
/// <remarks>
/// 摘要:
///     把当前1指定视图页中Jquery DataTables插件和表单中的相关数据信息赋值给该纪录及其基纪录实例,为内存逻辑分页操作提供数据支撑。
/// </remarks>
public record ExamSearchModel : BaseSearchModel
    {
#region 拷贝构造方法
/// <summary>
/// 【拷贝构造方法】
/// </summary
/// <remarks>
/// 摘要:
///     实例化该纪录时,为列表接口类型的属性成员实例分配内存空间。
/// </remarks>
public ExamSearchModel()
        {
            SpecialtySelectList = new List<SelectListItem>();
            GradeSelectList = new List<SelectListItem>();
            CategorySelectList = new List<SelectListItem>();
        }
#endregion
#region 属性--查询
/// <summary>
/// 【学号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置学生实体1个指定实例的学号。
/// </remarks>
        [Display(Name = "学号")]
public string Code { get; set; }
/// <summary>
/// 【姓名】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置1个指定学生的姓名。
/// </remarks>
        [Display(Name = "姓名")]
public string Name { get; set; }
/// <summary>
/// 【专业编号】
/// <remarks>
/// </summary>
/// 摘要:
///     获取/设置专业实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "专业")]
public int SpecialtyId { get; set; }
/// <summary>
/// 【年级编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置年级实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "年级")]
public int GradeId { get; set; }
/// <summary>
/// 【班级编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置班级实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "班级")]
public int CategoryId { get; set; }
/// <summary>
/// 【专业选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把专业实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> SpecialtySelectList { get; set; }
/// <summary>
/// 【年级选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把年级实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> GradeSelectList { get; set; }
/// <summary>
/// 【班级选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把班级实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> CategorySelectList { get; set; }
#endregion
    }
3.1.4 ExamModel.cs(表格渲染、添加和修改渲染显示)
/// <summary>
/// 【考试模型--纪录】
/// </summary>
/// <remarks>
/// 摘要:
///     把内存逻辑分页实例中的每1项实例,依次转换存储到该模型纪录的1个指定实例中,为Jquery DataTables插件表中每1行的渲染显示提供数据支撑。
/// </remarks>
public record ExamModel
    {
#region 拷贝构造方法
/// <summary>
/// 【拷贝构造方法】
/// </summary
/// <remarks>
/// 摘要:
///     实例化该纪录时,为列表接口类型的属性成员实例分配内存空间。
/// </remarks>
public ExamModel()
        {
            SpecialtySelectList = new List<SelectListItem>();
            GradeSelectList = new List<SelectListItem>();
            CategorySelectList = new List<SelectListItem>();
        }
#endregion
#region 属性--表格
/// <summary>
/// 【编号】
/// <remarks>
/// 摘要:
///     获取/设置学生实体1个指定实例的整型编号值。
/// </remarks>
/// </summary>
public int Id { get; set; }
/// <summary>
/// 【学号】
/// <remarks>
/// 摘要:
///     获取/设置学生实体1个指定实例的学号。
/// </remarks>
/// </summary>
        [Display(Name = "学号")] 
public string Code { get; set; }
/// <summary>
/// 【姓名】
/// <remarks>
/// 摘要:
///     获取/设置1个指定学生的姓名。
/// </remarks>
/// </summary>
        [Display(Name = "姓名")]
public string StudentName { 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; }
/// <summary>
/// 【课程名称】
/// <remarks>
/// 摘要:
///     获取/设置课程实体1个指定实例的名称。
/// </remarks>
/// </summary>
public string CourseName { get; set; }
/// <summary>
/// 【名称】
/// <remarks>
/// 摘要:
///     获取/设置1个指定考试的名称。
/// </remarks>
/// </summary>
        [Display(Name = "考试名称")]
public string Name { get; set; }
/// <summary>
/// 【成绩】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置1个指定成绩实例中的成绩整型值。
/// </remarks>
        [Display(Name = "成绩")]
public int? Score { get; set; }
/// <summary>
/// 【备注】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置1个指定成绩实例中的备注信息。
/// </remarks>
        [Display(Name = "备注")]
public string Comment { get; set; }
#endregion
#region 属性--添加/编辑
/// <summary>
/// 【专业编号】
/// <remarks>
/// </summary>
/// 摘要:
///     获取/设置专业实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "专业")]
public int SpecialtyId { get; set; }
/// <summary>
/// 【年级编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置年级实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "年级")]
public int GradeId { get; set; }
/// <summary>
/// 【班级编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置班级实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "班级")]
public int CategoryId { get; set; }
/// <summary>
/// 【班级编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置班级实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "课程")]
public int CourseId { get; set; }
/// <summary>
/// 【学生编号】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置班级实体1个指定实例的整型编号值。
/// </remarks>
        [Display(Name = "姓名")]
public int StudentId { get; set; }
/// <summary>
/// 【专业选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把专业实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> SpecialtySelectList { get; set; }
/// <summary>
/// 【年级选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把年级实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> GradeSelectList { get; set; }
/// <summary>
/// 【班级选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把班级实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> CategorySelectList { get; set; }
/// <summary>
/// 【课程选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把课程实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> CourseSelectList { get; set; }
/// <summary>
/// 【学生选择列表实例】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置把学生实体的所有下拉列表实例,存储到列表接口实例中。
/// </remarks>
public IList<SelectListItem> StudentSelectList { get; set; }
#endregion
    }
3.1.5 PagedListModel(DataTables渲染)
///<typeparam name="T">泛型类型实例(1个指定模型纪录的类型实例)。</typeparam>
/// <summary>
/// 【渲染显示分页模型--纪录】
/// </summary>
/// <remarks>
/// 摘要:
///     把内存逻辑分页实例中的每1项实例,依次转换存储到指定模型纪录的1个指定实例中后,把这些转换后的模型纪录所有的实例,
/// 存储到该纪录实例的枚举数接口实例中,为Jquery DataTables插件表中每1行的渲染显示提供数据支撑。
/// 说明:
///     模型纪录类型实例的指定,是通过泛型“<T>”的实现的。
/// </remarks>
public record PagedListModel<T>
    {
#region 属性--Jquery DataTables插件渲染显示基本数据支撑
/// <summary>
/// 【数据】
/// </summary>
/// <remarks>
/// 摘要:
///     获取/设置1个枚举数接口实例,该实例存储着1个指定泛型“<T>”类中的所有的实例,为Jquery DataTable插件中的表格渲染显示提供数据支撑。
/// 注意:
///     因为Jquery DataTable插件是通过Jquery Ajax操作在浏览器中渲染出1指定类中的所有实例数据,所以该属性必须被命名为“Data”,也只能被命名为“Data”,否则浏览器中将不会渲染出这些数据。
/// </remarks>
public IEnumerable<T> Data { get; set; }
/// <summary>
/// 【绘制】
/// </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>
        [JsonProperty(PropertyName = "draw")]
public string Draw { get; set; }
/// <summary>
/// 【过滤总计】
/// </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>
        [JsonProperty(PropertyName = "recordsFiltered")]
public int RecordsFiltered { get; set; }
/// <summary>
/// 【总计】
/// </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>
        [JsonProperty(PropertyName = "recordsTotal")]
public int RecordsTotal { get; set; }
#endregion
#region 属性--Jquery DataTables插件渲染显示分页数据支撑
/// <summary>
/// 【开始】
/// <remarks>
/// 摘要:
///     获取/设置1个指定的Jquery DataTable插件当前显示页面,Start>=0。  
/// </remarks>
/// </summary>
public int Start { get; set; }
/// <summary>
/// 【长度】
/// <remarks>
/// 摘要:
///     获取/设置1个指定的Jquery DataTable插件每页最多的行数值:(默认值)10,Length>=1。  
/// </remarks>
/// </summary>
public int Length { get; set; }
#endregion
}
3.2 分页、增、修、删
3.2.1 LogicRenderController.cs
using Data;
using Data.Domain.Students;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using SqlSugar;
using Web.Extensions;
using Web.Models;
namespace Web.Controllers
{
    public class LogicRenderController : Controller
    {
        private readonly SqlSugarContext _context;
        public LogicRenderController(SqlSugarContext context)
        {
            _context = context;
        }
        #region 方法--私有/保护
        /// <param name="studentSearchModel">1个指定的学生查询模型实例。</param>
        /// <summary>
        /// 【预处理成绩表单】
        /// <remarks>
        /// 摘要:
        ///     通过参数实例,初始化设置页面中的查询表单和Jquery DataTable插件。
        /// </remarks>
        /// </summary>
        protected virtual ExamSearchModel PrepareScoreSearchModel(ExamSearchModel model)
        {
            model.SpecialtySelectList = PrepareSpecialty();
            model.GradeSelectList = PrepareGrade();
            model.CategorySelectList = new List<SelectListItem>();
            return model;
        }
        protected IList<SelectListItem> PrepareSpecialty(int id=0, bool isDefaultItem = true)
        {
            List<Specialty> _specialtyList = _context.SugarScope.Queryable<Specialty>().ToList();
            return _specialtyList.AddSelectList(nameof(Specialty.Name), nameof(Specialty.Id), isDefaultItem);
        }
        protected IList<SelectListItem> PrepareGrade(int id=0, bool isDefaultItem = true)
        {
            List<Grade> _gradeList = _context.SugarScope.Queryable<Grade>().ToList();
            return _gradeList.AddSelectList(nameof(Grade.Name), nameof(Grade.Id), isDefaultItem);
        }
        protected IList<SelectListItem> PrepareCategory(int id = 0, bool isDefaultItem = true)
        {
            List<Category> _categoryList = _context.SugarScope.Queryable<Category>().ToList();
            return _categoryList.AddSelectList(nameof(Category.Name), nameof(Category.Id), isDefaultItem);
        }
        protected IList<SelectListItem> PrepareStudent(int id = 0, bool isDefaultItem = true)
        {
            List<Student> _studentList = _context.SugarScope.Queryable<Student>().ToList();
            return _studentList.AddSelectList(nameof(Student.Name), nameof(Student.Id), isDefaultItem);
        }
        protected IList<SelectListItem> PrepareCourse(int id = 0, bool isDefaultItem = true)
        {
            List<Course> _courseList = _context.SugarScope.Queryable<Course>().ToList();
            return _courseList.AddSelectList(nameof(Course.Name), nameof(Course.Id), isDefaultItem);
        }
        #endregion
        public IActionResult Index()
        {
            ExamSearchModel _model = PrepareScoreSearchModel(new ExamSearchModel());
            return View(_model);
        }
        public IActionResult CreatePopup()
        {
            ExamModel _examModel = new ExamModel();
            _examModel.SpecialtySelectList = PrepareSpecialty(0, true);
            _examModel.GradeSelectList = PrepareGrade(0, true);
            _examModel.CourseSelectList = PrepareCourse(0, true);
            _examModel.CategorySelectList = new List<SelectListItem>();
            _examModel.StudentSelectList = new List<SelectListItem>();
            ViewBag.RefreshPage = false;
            return View(_examModel);
        }
        [HttpPost]
        public async Task<IActionResult> CreatePopup(ExamModel model)
        {
            Exam _exam = new Exam();
            _exam.Score = model.Score;
            _exam.Comment= model.Comment;
            _exam.Name = model.Name;
            _exam.CourseId = model.CourseId;
            _exam.StudentId = model.StudentId;
           await _context.SugarScope.Insertable(_exam).ExecuteReturnEntityAsync();
            model.SpecialtySelectList = PrepareSpecialty(0, true);
            model.GradeSelectList = PrepareGrade(0, true);
            model.CourseSelectList = PrepareCourse(0, true);
            model.CategorySelectList = new List<SelectListItem>();
            model.StudentSelectList = new List<SelectListItem>();
            ViewBag.RefreshPage = true;
            return View(model);
        }
        public async Task<IActionResult> EditPopup(int id)
        {
            Exam _model = await _context.SugarScope.Queryable<Exam>()
               .Includes(score => score.CourseSingleton)//1层级联
               .Includes(
                   exam => exam.StudentSingleton,
                   student => student.SpecialtySingleton)//2层级联
               .Includes(
                   exam => exam.StudentSingleton,
                   student => student.CategorySingleton)//2层级联
                .Includes(
                   exam => exam.StudentSingleton,
                   student => student.CategorySingleton,
                   category => category.GradeSingleton)//3层级联
                .InSingleAsync(id);
            ExamModel _examModel = new ExamModel();
            _examModel.Id = id;
            _examModel.Code = _model.StudentSingleton.Code;
            _examModel.StudentName = _model.StudentSingleton.Name;
            _examModel.Name = _model.Name;
            _examModel.Score = _model.Score;
            _examModel.Comment = _model.Comment;
            _examModel.SpecialtyId = _model.StudentSingleton.SpecialtyId;
            _examModel.GradeId = _model.StudentSingleton.CategorySingleton.GradeId;
            _examModel.CourseId = _model.CourseId;
            _examModel.CategoryId = _model.StudentSingleton.CategoryId;
            _examModel.StudentId = _model.StudentId;
            _examModel.SpecialtySelectList = PrepareSpecialty(_examModel.SpecialtyId, false);
            _examModel.GradeSelectList = PrepareGrade(_examModel.GradeId, false);
            _examModel.CourseSelectList = PrepareCourse(_examModel.CourseId, false);
            _examModel.CategorySelectList = PrepareCategory(_examModel.CategoryId, false);
            _examModel.StudentSelectList = PrepareStudent(_examModel.StudentId, false);
            ViewBag.RefreshPage = false;
            return View(_examModel);
        }
        [HttpPost]
        public async Task<IActionResult> EditPopup(ExamModel model)
        {
            Exam _model = new Exam();
            _model.Id = model.Id;
            _model.Name = model.Name;
            _model.Comment = model.Comment;
            _model.CourseId = model.CourseId;
            _model.StudentId = model.StudentId;
            _model.Score = model.Score;
           await _context.SugarScope.Updateable(_model).ExecuteCommandAsync();
            _model = await _context.SugarScope.Queryable<Exam>()
            .Includes(score => score.CourseSingleton)//1层级联
            .Includes(
                exam => exam.StudentSingleton,
                student => student.SpecialtySingleton)//2层级联
            .Includes(
                exam => exam.StudentSingleton,
                student => student.CategorySingleton)//2层级联
             .Includes(
                exam => exam.StudentSingleton,
                student => student.CategorySingleton,
                category => category.GradeSingleton)//3层级联
             .InSingleAsync(model.Id);
            model.SpecialtyId = _model.StudentSingleton.SpecialtyId;
            model.GradeId = _model.StudentSingleton.CategorySingleton.GradeId;
            model.CourseId = _model.CourseId;
            model.CategoryId = _model.StudentSingleton.CategoryId;
            model.StudentId = _model.StudentId;
            model.SpecialtySelectList = PrepareSpecialty(model.SpecialtyId, false);
            model.GradeSelectList = PrepareGrade(model.GradeId, false);
            model.CourseSelectList = PrepareCourse(model.CourseId, false);
            model.CategorySelectList = PrepareCategory(model.CategoryId, false);
            model.StudentSelectList = PrepareStudent(model.StudentId, false);
            ViewBag.RefreshPage = true;
            return View(model);
        }
        #region 方法--Ajax
        /// <param name="gradeId">年级实体1个指定实例的整型编号值,默认值:0,即班级下拉框控件没有任何项。</param>
        /// <summary>
        /// 【班级下拉控件局部刷新】
        /// </summary>
        /// <remarks>
        /// 摘要:
        ///     通过参数实例,实现年级下拉框控件联动局部刷新班级下拉框控件。
        /// </remarks>
        /// <returns>
        ///     班级下拉控件局部刷新渲染所需要的JSON编码格式的数据。
        /// </returns>
        [HttpPost]
        public async Task<IActionResult> CategorySelectListAjax(int? gradeId = 0)
        {
            List<SelectListItem> _selectList = new List<SelectListItem>();
            if (gradeId != 0)
            {
                List<Category> _categoryList = await _context.SugarScope.Queryable<Category>()
                    .Where(categary => categary.GradeId == gradeId)
                    .ToListAsync();
                _selectList = _categoryList.AddSelectList(nameof(Category.Name), nameof(Category.Id));
            }
            return Json(_selectList);
        }
        /// <param name="categaryId">学生实体1个指定实例的整型编号值,默认值:0,即学生下拉框控件没有任何项。</param>
        /// <summary>
        /// 【学生下拉控件局部刷新】
        /// </summary>
        /// <remarks>
        /// 摘要:
        ///     通过参数实例,实现班级下拉框控件联动局部刷新学生下拉框控件。
        /// </remarks>
        /// <returns>
        ///     班级下拉控件局部刷新渲染所需要的JSON编码格式的数据。
        /// </returns>
        [HttpPost]
        public async Task<IActionResult> StudentSelectListAjax(int? categaryId = 0)
        {
            List<SelectListItem> _selectList = new List<SelectListItem>();
            if (categaryId != 0)
            {
                List<Student> _studentList = await _context.SugarScope.Queryable<Student>()
                    .Where(student => student.CategoryId == categaryId).ToListAsync();
                _selectList = _studentList.AddSelectList(nameof(Student.Name), nameof(Student.Id));
            }
            return Json(_selectList);
        }
        /// <param name="studentId">学生实体1个指定实例的整型编号值,默认值:0,即学生编号控件内没有任何值。</param>
        /// <summary>
        /// 【学生编号控件局部刷新】
        /// </summary>
        /// <remarks>
        /// 摘要:
        ///     通过参数实例,实现学生下拉框控件联动局部刷学生编号控件。
        /// </remarks>
        /// <returns>
        ///     学生编号控件局部刷新渲染所需要的JSON编码格式的数据。
        /// </returns>
        [HttpPost]
        public async Task<IActionResult> CodeSelectListAjax(int? studentId = 0)
        {
            Student _model = new Student();
            if (studentId != 0)
            {
                _model = await _context.SugarScope.Queryable<Student>().InSingleAsync(studentId);
            }
            return Json(_model.Code);
        }
        /// <param name="gradeId">年级实体1个指定实例的整型编号值,默认值:0,即班级下拉框控件没有任何项。</param>
        /// <summary>
        /// 【班级下拉控件局部刷新】
        /// </summary>
        /// <remarks>
        /// 摘要:
        ///     通过参数实例,实现年级下拉框控件联动局部刷新班级下拉框控件。
        /// </remarks>
        /// <returns>
        ///     班级下拉控件局部刷新渲染所需要的JSON编码格式的数据。
        /// </returns>
        //public IActionResult StudentSelectListAjax(int? gradeId = 0)
        //{
        //    List<SelectListItem> _selectList = new List<SelectListItem>();
        //    if (gradeId != 0)
        //    {
        //        List<Student> _studentList = _context.SugarScope.Queryable<Student>().ToList();
        //        _studentList = _studentList.Where(categary => categary.GradeId == gradeId).ToList();
        //        _selectList = _categoryList.AddSelectList(nameof(Student.Name), nameof(Student.Id));
        //    }
        //    return Json(_selectList);
        //}
        /// <summary>
        /// 【成绩表格局部刷新】
        /// </summary>
        /// <remarks>
        /// 摘要:
        ///     通过参数实例,实现成绩表格“DataTables”插件的局部刷新渲染显示。
        /// </remarks>
        /// <returns>
        ///    成绩表格“DataTables”插件的局部刷新渲染显示,提供JSON编码格式的数据支撑。
        /// </returns>
        [HttpPost]
        public async Task<IActionResult> ScoreListAjax(ExamSearchModel model)
        {
            //获取DataTable插件当前所设定的排序方式。
            OrderByType _orderByType = model.Dir.Equals("asc") ? OrderByType.Asc : OrderByType.Desc;
            //指定当前DataTable插件,将由那个表头字段进行排序操作。
            bool _isIdOrder = model.Column.ToLower().Equals(nameof(ExamModel.Id).ToLower());
            bool _isStudentOrder = model.Column.ToLower().Equals(nameof(ExamModel.StudentName).ToLower());
            bool _isCodeOrder = model.Column.ToLower().Equals(nameof(ExamModel.Code).ToLower());
            bool _isScoreOrder = model.Column.ToLower().Equals(nameof(ExamModel.Score).ToLower());
            //获取考试表排序后的所有实例。
            ISugarQueryable<Exam> _scoreQueryable = _context.SugarScope.Queryable<Exam>()
               .Includes(score => score.CourseSingleton)//1层级联
               .Includes(
                   exam => exam.StudentSingleton,
                   student => student.SpecialtySingleton)//2层级联
               .Includes(
                   exam => exam.StudentSingleton,
                   student => student.CategorySingleton)//2层级联
                .Includes(
                   exam => exam.StudentSingleton,
                   student => student.CategorySingleton,
                   category => category.GradeSingleton)//3层级联
               .OrderByIF(_isIdOrder, itme => itme.Id, _orderByType)
               .OrderByIF(_isStudentOrder, itme => itme.StudentSingleton.Name, _orderByType)
               .OrderByIF(_isCodeOrder, itme => itme.StudentSingleton.Code, _orderByType)
               .OrderByIF(_isScoreOrder, itme => itme.Score, _orderByType);
            //获取符合指定查询条件考试表中的所有实例。
            _scoreQueryable = _scoreQueryable
                .WhereIF(!string.IsNullOrEmpty(model.Code), itme => itme.StudentSingleton.Code == model.Code)
                .WhereIF(!string.IsNullOrEmpty(model.Name), itme => itme.StudentSingleton.Name.Contains(model.Name))
                .WhereIF(model.SpecialtyId >= 1, itme => itme.StudentSingleton.SpecialtyId == model.SpecialtyId)
                .WhereIF(model.CategoryId >= 1, itme => itme.StudentSingleton.CategoryId == model.CategoryId)
                .WhereIF(model.GradeId >= 1, itme => itme.StudentSingleton.CategorySingleton.GradeSingleton.Id== model.GradeId);
            RefAsync<int> total = 0;//REF和OUT不支持异步,想要真的异步这是最优解。
            total = await _scoreQueryable.CountAsync();//获取符合指定查询条件考试表中的所有实例的总计值。
            //通过“SqlSugarCore”中间件的内置方法,对内存中所存储的所有考试实例,进行逻辑分页操作,并把该指定逻辑1页内的所有数据,存储到列表实例中。
            List<Exam> _examList = await _scoreQueryable
                .ToPageListAsync(model.Start / model.Length, model.Length, total);
            //把指定逻辑1页内的所有数据,依次转换后存储到相应的模型实例中,并把这些模型实例,存储到列表实例中,该列表实例将为DataTable插件中表格的渲染显示提供支撑。
            List<ExamModel> _examModelList = _examList.Select(item => new ExamModel()
            {
                Id = item.Id,
                Code = item.StudentSingleton.Code,
                StudentName = item.StudentSingleton.Name,
                SpecialtyName = item.StudentSingleton.SpecialtySingleton.Name,
                GradeName = item.StudentSingleton.CategorySingleton.GradeSingleton.Name,
                CategoryName = item.StudentSingleton.CategorySingleton.Name,
                CourseName = item.CourseSingleton.Name,
                Name = item.Name,
                Score = item.Score,
                Comment = item.Comment,
            }).ToList();
            //实例化分页列表模型实例,该实例将为DataTable插件整体性的渲染显示提供支撑。
            PagedListModel<ExamModel> _pagedListModel = new PagedListModel<ExamModel>();
            _pagedListModel.Draw = model.Draw;
            _pagedListModel.Start = model.Start;
            _pagedListModel.Length = model.Length;
            _pagedListModel.RecordsTotal = total;
            _pagedListModel.RecordsFiltered = total;
            _pagedListModel.Data = _examModelList;
            return Json(_pagedListModel);
        }
        [HttpPost]
        public async Task<IActionResult> DelelteExamArrayAjax(int[] IdArray)
        {
            try
            {
                int result = 0;
                result = await _context.SugarScope.Deleteable<Exam>().In(IdArray).ExecuteCommandAsync();
                if (result > 0)
                {
                    return Json(new { isDeleted = true });
                }
                else
                {
                    return Json(new { isDeleted = false });
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion
    }
}
3.2.2 LogicRender\Index.cshtml
@model Web.Models.ExamModel
@{
    Layout = "_PopupLayout";
    ViewData["Title"] = "成绩编辑";
}
<div class="row mt-4 me-3">
<div class="col-md-12">
<form asp-action="CreatePopup"
             asp-route-btnId="@Context.Request.Query["btnId"]">
<div class="row mb-3">
<label asp-for="SpecialtyId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="SpecialtyId" class="form-select" asp-items="Model.SpecialtySelectList"></select>
</div>
</div>
<div class="row mb-3">
<label asp-for="CourseId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="CourseId" class="form-select" asp-items="Model.CourseSelectList"></select>
</div>
</div>
<div class="row mb-3">
<label asp-for="GradeId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="GradeId" class="form-select" asp-items="Model.GradeSelectList"></select>
</div>
</div>
<div class="row mb-3 d-none" id="categoryRow">
<label asp-for="CategoryId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="CategoryId" class="form-select" asp-items="Model.CategorySelectList"></select>
</div>
</div>
<div class="row mb-3 d-none" id="studentRow">
<label asp-for="StudentId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="StudentId" class="form-select" asp-items="Model.StudentSelectList"></select>
</div>
</div>
<div class="row mb-3 d-none" id="codeRow">
<label asp-for="Code" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Code" readonly class="form-control-plaintext" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Name" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Score" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Score" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Comment" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<textarea asp-for="Comment" class="form-control" style="height:100px"></textarea>
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="提 交" class="btn btn-primary mt-3" />
</div>
</form>
</div>
</div>
@section Scripts 
{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
//年级下拉框控件联动局部刷新班级下拉框控件。
        $('#GradeId').on('change', function () {
var gradeId = $(this).val();
            $.ajax({
                url: "/LogicRender/CategorySelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"gradeId": gradeId,
                },
                success: function (data) {
if (gradeId == 0) {
                        $("#categoryRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#categoryRow").removeClass('d-none'); //显示
                    }
                    $('#CategoryId').empty();
                    $.each(data,
function (id, option) {
                            $('#CategoryId').append($('<option></option>').val(option.value).html(option.text));
                        });
                }
            });
        });
//班级下拉框控件联动局部刷新学生下拉框控件。
        $('#CategoryId').on('change', function () {
var categaryId = $(this).val();
            $.ajax({
                url: "/LogicRender/StudentSelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"categaryId": categaryId,
                },
                success: function (data) {
if (categaryId == 0) {
                        $("#studentRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#studentRow").removeClass('d-none'); //显示
                    }
                    $('#StudentId').empty();
                    $.each(data,
function (id, option) {
                            $('#StudentId').append($('<option></option>').val(option.value).html(option.text));
                        });
                }
            });
        });
//学生下拉框控件联动局部刷新学生编号控件。
        $('#StudentId').on('change', function () {
var studentId = $(this).val();
          
            $.ajax({
                url: "/LogicRender/CodeSelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"studentId": studentId,
                },
                success: function (data) {
if (studentId == 0) {
                        $("#codeRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#codeRow").removeClass('d-none'); //显示
                    }
                    $('#Code').val(data);
                }
            });
        });
//在数据持久化到指定表后,自动通过父窗口中指定按钮标签的单击事件,自动关闭当前弹出窗口。
if ("@ViewBag.RefreshPage" == "True") {
//获取父窗口中指定按钮标签。
//window.opener.document.getElementById("@Context.Request.Query["btnId"]").click();
            window.opener.$("#@Context.Request.Query["btnId"]").click();
            window.close();
        }
</script>
}
3.2.3 LogicRender\CreatePopup.cshtml
@model Web.Models.ExamModel
@{
    Layout = "_PopupLayout";
    ViewData["Title"] = "成绩编辑";
}
<div class="row mt-4 me-3">
<div class="col-md-12">
<form asp-action="CreatePopup"
             asp-route-btnId="@Context.Request.Query["btnId"]">
<div class="row mb-3">
<label asp-for="SpecialtyId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="SpecialtyId" class="form-select" asp-items="Model.SpecialtySelectList"></select>
</div>
</div>
<div class="row mb-3">
<label asp-for="CourseId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="CourseId" class="form-select" asp-items="Model.CourseSelectList"></select>
</div>
</div>
<div class="row mb-3">
<label asp-for="GradeId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="GradeId" class="form-select" asp-items="Model.GradeSelectList"></select>
</div>
</div>
<div class="row mb-3 d-none" id="categoryRow">
<label asp-for="CategoryId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="CategoryId" class="form-select" asp-items="Model.CategorySelectList"></select>
</div>
</div>
<div class="row mb-3 d-none" id="studentRow">
<label asp-for="StudentId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="StudentId" class="form-select" asp-items="Model.StudentSelectList"></select>
</div>
</div>
<div class="row mb-3 d-none" id="codeRow">
<label asp-for="Code" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Code" readonly class="form-control-plaintext" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Name" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Score" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Score" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Comment" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<textarea asp-for="Comment" class="form-control" style="height:100px"></textarea>
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="提 交" class="btn btn-primary mt-3" />
</div>
</form>
</div>
</div>
@section Scripts 
{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
//年级下拉框控件联动局部刷新班级下拉框控件。
        $('#GradeId').on('change', function () {
var gradeId = $(this).val();
            $.ajax({
                url: "/LogicRender/CategorySelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"gradeId": gradeId,
                },
                success: function (data) {
if (gradeId == 0) {
                        $("#categoryRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#categoryRow").removeClass('d-none'); //显示
                    }
                    $('#CategoryId').empty();
                    $.each(data,
function (id, option) {
                            $('#CategoryId').append($('<option></option>').val(option.value).html(option.text));
                        });
                }
            });
        });
//班级下拉框控件联动局部刷新学生下拉框控件。
        $('#CategoryId').on('change', function () {
var categaryId = $(this).val();
            $.ajax({
                url: "/LogicRender/StudentSelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"categaryId": categaryId,
                },
                success: function (data) {
if (categaryId == 0) {
                        $("#studentRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#studentRow").removeClass('d-none'); //显示
                    }
                    $('#StudentId').empty();
                    $.each(data,
function (id, option) {
                            $('#StudentId').append($('<option></option>').val(option.value).html(option.text));
                        });
                }
            });
        });
//学生下拉框控件联动局部刷新学生编号控件。
        $('#StudentId').on('change', function () {
var studentId = $(this).val();
          
            $.ajax({
                url: "/LogicRender/CodeSelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"studentId": studentId,
                },
                success: function (data) {
if (studentId == 0) {
                        $("#codeRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#codeRow").removeClass('d-none'); //显示
                    }
                    $('#Code').val(data);
                }
            });
        });
//在数据持久化到指定表后,自动通过父窗口中指定按钮标签的单击事件,自动关闭当前弹出窗口。
if ("@ViewBag.RefreshPage" == "True") {
//获取父窗口中指定按钮标签。
//window.opener.document.getElementById("@Context.Request.Query["btnId"]").click();
            window.opener.$("#@Context.Request.Query["btnId"]").click();
            window.close();
        }
</script>
}
3.2.4 LogicRender\EditPopup.cshtml
@model Web.Models.ExamModel
@{
    Layout = "_PopupLayout";
    ViewData["Title"] = "成绩编辑";
}
<div class="row mt-4 me-3">
<div class="col-md-12">
<form asp-action="EditPopup"
           asp-route-Id="@Context.Request.Query["Id"]"
           asp-route-btnId="@Context.Request.Query["btnId"]">
           
<div class="row mb-3">
<label asp-for="SpecialtyId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="SpecialtyId" class="form-select" asp-items="@(new SelectList(Model.SpecialtySelectList,"Value","Text", Model.SpecialtyId))"></select>
</div>
</div>
<div class="row mb-3">
<label asp-for="CourseId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="CourseId" class="form-select" asp-items="@(new SelectList(Model.CourseSelectList,"Value","Text", Model.CourseId))"></select>
</div>
</div>
<div class="row mb-3">
<label asp-for="GradeId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="GradeId" class="form-select" asp-items="@(new SelectList(Model.GradeSelectList,"Value","Text", Model.GradeId))"></select>
</div>
</div>
<div class="row mb-3" id="categoryRow">
<label asp-for="CategoryId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="CategoryId" class="form-select" asp-items="@(new SelectList(Model.CategorySelectList,"Value","Text", Model.CategoryId))"></select>
</div>
</div>
<div class="row mb-3" id="studentRow">
<label asp-for="StudentId" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<select asp-for="StudentId" class="form-select" asp-items="@(new SelectList(Model.StudentSelectList,"Value","Text", Model.SpecialtyId))"></select>
</div>
</div>
<div class="row mb-3" id="codeRow">
<label asp-for="Code" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Code" readonly class="form-control-plaintext" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Name" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Score" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<input asp-for="Score" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label asp-for="Comment" class="col-sm-2 col-form-label text-end"></label>
<div class="col-sm-10">
<textarea asp-for="Comment" class="form-control" style="height:100px"></textarea>
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="提 交" class="btn btn-primary mt-3" />
</div>
</form>
</div>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
//年级下拉框控件联动局部刷新班级下拉框控件。
        $('#GradeId').on('change', function () {
var gradeId = $(this).val();
            $.ajax({
                url: "/LogicRender/CategorySelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"gradeId": gradeId,
                },
                success: function (data) {
if (gradeId == 0) {
                        $("#categoryRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#categoryRow").removeClass('d-none'); //显示
                    }
                    $('#CategoryId').empty();
                    $.each(data,
function (id, option) {
                            $('#CategoryId').append($('<option></option>').val(option.value).html(option.text));
                        });
                }
            });
        });
//班级下拉框控件联动局部刷新学生下拉框控件。
        $('#CategoryId').on('change', function () {
var categaryId = $(this).val();
            $.ajax({
                url: "/LogicRender/StudentSelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"categaryId": categaryId,
                },
                success: function (data) {
if (categaryId == 0) {
                        $("#studentRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#studentRow").removeClass('d-none'); //显示
                    }
                    $('#StudentId').empty();
                    $.each(data,
function (id, option) {
                            $('#StudentId').append($('<option></option>').val(option.value).html(option.text));
                        });
                }
            });
        });
//学生下拉框控件联动局部刷新学生编号控件。
        $('#StudentId').on('change', function () {
var studentId = $(this).val();
            $.ajax({
                url: "/LogicRender/CodeSelectListAjax",
                type: "POST",
                datatype: "JSON",
                cache: false,  //禁用缓存。
                data:
                {
"studentId": studentId,
                },
                success: function (data) {
if (studentId == 0) {
                        $("#codeRow").addClass('d-none'); // 隐藏
                    }
else {
                        $("#codeRow").removeClass('d-none'); //显示
                    }
                    $('#Code').val(data);
                }
            });
        });
//在数据持久化到指定表后,自动通过父窗口中指定按钮标签的单击事件,自动关闭当前弹出窗口。
if ("@ViewBag.RefreshPage" == "True") {
//获取父窗口中指定按钮标签。
//window.opener.document.getElementById("@Context.Request.Query["btnId"]").click();
            window.opener.$("#@Context.Request.Query["btnId"]").click();
            window.close();
        }
</script>
}

3.2.5 内存逻辑分页,之DataTable渲染显示原理

postgresql offset分页后的总数 sqlsugar 分页_内置逻辑分页

 对以上功能更为具体实现和注释见:22-10-06-05_SqlSugarAcquaintance(初识SqlSugarCore之内置逻辑分页)。