You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 6整合分页与排序过滤触发NullReferenceException求助

问题描述

完成分页功能开发后,加入旧代码的排序与过滤逻辑,程序抛出以下错误:

NullReferenceException: Object reference not set to an instance of an object.

怀疑问题出在分页使用Post方法、排序过滤使用Get方法,但不清楚具体修复方案。

控制器代码

public async Task<IActionResult> Index()
{
    return View(await GetStudentList(1));
}

[HttpPost]
public async Task<IActionResult> Index(int currentPageIndex)
{
    return View(await GetStudentList(currentPageIndex));
}

// GET: Students
public IActionResult IndexProses(string sortOrder, StudentVM model)
{
    ViewData["CurrentSort"] = sortOrder;
    ViewData["NameSortParm"] = sortOrder == "name" ? "name_desc" : "name";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    ViewData["FirstSortParm"] = String.IsNullOrEmpty(sortOrder) ? "first_desc" : "";

    ViewData["CurrentFilter"] = model.FirstMidName;
    ViewData["CurrentFilter2"] = model.LastName;
    ViewData["CurrentFilter3"] = model.EnrollmentDateFrom;
    ViewData["CurrentFilter3"] = model.EnrollmentDateUntil;

    var students = from s in _context.Students
                   select s;

    // sorting // 
    students = sortOrder switch
    {
        "name" => students.OrderBy(s => s.LastName),// asc last name
        "name_desc" => students.OrderByDescending(s => s.LastName),//desc last name
        "first_desc" => students.OrderByDescending(s => s.FirstMidName),// desc first name
        "Date" => students.OrderBy(s => s.EnrollmentDate),// asc date
        "date_desc" => students.OrderByDescending(s => s.EnrollmentDate),// desc date
        _ => students.OrderBy(s => s.FirstMidName),// asc first name
    };

    // filtering
    if (model.EnrollmentDateFrom != null && model.EnrollmentDateUntil != null)
    {
        students = students.Where(s => s.EnrollmentDate >= model.EnrollmentDateFrom && s.EnrollmentDate <= model.EnrollmentDateUntil);
    }
    if (!String.IsNullOrEmpty(model.LastName))
    {
        students = students.Where(s => s.LastName.Contains(model.LastName));
    }
    if (!String.IsNullOrEmpty(model.FirstMidName))
    {
        students = students.Where(s => s.FirstMidName.Contains(model.FirstMidName));
    }
    return View("Index", model);
} 

private async Task<StudentVM> GetStudentList(int currentPage)
{
    int maxRowsPerPage = 5;
    StudentVM model = new StudentVM();
    model.StundentList = await (from s in _context.Students select s)
        .Skip((currentPage - 1) * maxRowsPerPage)
        .Take(maxRowsPerPage).ToListAsync();

    double pageCount = (double)((decimal)_context.Students.Count() / Convert.ToDecimal(maxRowsPerPage));

    model.pageCount = (int)Math.Ceiling(pageCount);
    model.currentPageIndex = currentPage;
    return model;
}

视图代码(Index.cshtml)

@model ContosoUniversity.Models.SchoolViewModels.StudentVM

@{
    ViewData["Title"] = "Students";
}

<style>
    table{
        margin-top: 20px;
    }

    .table a{
    text-decoration: none;
    }
    
    h2{
        margin-bottom: 20px;
    }

    .tbl{
        display: flex;
        gap: 1rem;
    }

    .box{
        display: flex;
        gap: 1rem;
        margin-bottom: 10px;
    }
</style>
<h2>Contoso University - Students</h2>

<form asp-action="IndexProses" asp-antiforgery="false" method="get">
    <div class="form-actions no-color">
        <h4>Search By</h4>
        <div class="box">
            <div class="search">
                <label asp-for="FirstMidName"></label>
                <input name="FirstMidName" type="text" value="@ViewData["CurrentFilter"]"/>
                <br />
                <span asp-validation-for="FirstMidName" class="text-danger"></span>
            </div>
            
            <div class="search">
                <label asp-for="LastName"></label>
                <input name="LastName" type="text" value="@ViewData["CurrentFilter2"]"/>
                <br />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>

            <div class="search">
                <label asp-for="EnrollmentDateFrom"></label>
                <input name="EnrollmentDateFrom" type="date" value="@ViewData["CurrentFilter3"]"/>
                <br />
                <span asp-validation-for="EnrollmentDateFrom" class="text-danger"></span>
            </div>

            <div class="search">
                <label asp-for="EnrollmentDateUntil"></label>
                <input name="EnrollmentDateUntil" type="date" value="@ViewData["CurrentFilter4"]"/>
                <br />
                <span asp-validation-for="EnrollmentDateUntil" class="text-danger"></span>
            </div>

            <input type="submit" value="Search" class="btn btn-primary" />
        </div>
    </div>
</form>

<div class="tbl">
    <a asp-action="Create" class="btn btn-primary">Create New [+]</a>
    <a asp-action="Index" class="btn btn-info text-white">Back to List</a>
</div>

<form asp-action="Index" asp-controller="Students" method="post">
    <table class="table table-hover">
        <thead>
            <tr class="table-light">
                <th class="table-light">
                    @Html.ActionLink("First Name", "IndexProses", new { sortOrder = ViewBag.FirstSortParm, FirstMidName = Model.FirstMidName, LastName = Model.LastName, EnrollmentDateFrom = Model.EnrollmentDateFrom, EnrollmentDateUntil = Model.EnrollmentDateUntil })
                </th>
                <th>
                @Html.ActionLink("Last Name", "IndexProses", new { sortOrder = ViewBag.NameSortParm, FirstMidName = Model.FirstMidName, LastName = Model.LastName, EnrollmentDateFrom = Model.EnrollmentDateFrom, EnrollmentDateUntil = Model.EnrollmentDateUntil })
                </th>
                <th>
                @Html.ActionLink("Enrollment Date", "IndexProses", new { sortOrder = ViewBag.DateSortParm, FirstMidName = Model.FirstMidName, LastName = Model.LastName, EnrollmentDateFrom = Model.EnrollmentDateFrom, EnrollmentDateUntil = Model.EnrollmentDateUntil })
                </th>
                <th>Opsi</th>
            </tr>
        </thead>
        <tbody class="opsi">
            @foreach (var item in Model.StundentList)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.FirstMidName)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.LastName)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.EnrollmentDate)
                    </td>
                    <td>
                        <a class="btn btn-warning text-white" asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                        <a class="btn btn-info text-white" asp-action="Details" asp-route-id="@item.ID">Details</a> |
                        <a class="btn btn-danger" asp-action="Delete" asp-route-id="@item.ID">Delete</a>
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <ul class="pagination">
        @for(int i=1; i<=Model.pageCount; i++)
        {
            <li class="page-item">
                @if(i != Model.currentPageIndex)
                {
                    <a class="page-link" href="javascript: PagerClick(@i);">@i</a>
                }
                else
                {
                    <a class="page-link">@i</a>
                }
            </li>
        }
    </ul>
    <input type="hidden" id="hfCurrentPageIndex" name="currentPageIndex"/>
</form>

<script type="text/javascript">
    function PagerClick(index)
    {
        document.getElementById("hfCurrentPageIndex").value=index;
        document.forms[0].submit();
    }
</script>

ViewModel代码

public class StudentVM : IValidatableObject
    {
        public int currentPageIndex { get; set; }
        public int pageCount { get; set; }
        public List<Student> StundentList { get; set; }

        [Display(Name = "Last Name :")]
        public string LastName { get; set; }

        [Display(Name = "First Name :")]
        public string FirstMidName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "Date From :")]
        public DateTime? EnrollmentDateFrom { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "Until :")]
        public DateTime? EnrollmentDateUntil { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (FirstMidName == null)
            {
                yield return new ValidationResult("input first name!", new[] { "FirstMidName" });
            }
            
            if (LastName == null)
            {
                yield return new ValidationResult("input last name!", new[] { "LastName" });
            }

            if (EnrollmentDateFrom == null)
            {
                yield return new ValidationResult("input date from!", new[] { "EnrollmentDateFrom" });
            }
            if (EnrollmentDateUntil == null)
            {
                yield return new ValidationResult("input date until!", new[] { "EnrollmentDateUntil" });
            }
        }
    }

修复方案

1. 核心问题分析

  • IndexProses返回视图时未给StudentVM的分页属性(StundentList、pageCount)赋值,导致视图访问Model属性时触发空引用异常。
  • 分页方法GetStudentList仅查询原始数据,未应用排序/过滤逻辑,与IndexProses完全脱节。
  • 分页用Post提交但未携带排序/过滤参数,切换页面后丢失筛选状态。

2. 具体修复步骤

步骤1:合并控制器逻辑,统一处理所有请求

删除IndexProses和HttpPost Index方法,用单个Index方法整合分页、排序、过滤逻辑:

public async Task<IActionResult> Index(int? currentPageIndex, string sortOrder, StudentVM model)
{
    // 初始化分页索引
    int page = currentPageIndex ?? 1;
    int maxRowsPerPage = 5;

    // 维护排序参数
    ViewData["CurrentSort"] = sortOrder;
    ViewData["NameSortParm"] = sortOrder == "name" ? "name_desc" : "name";
    ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
    ViewData["FirstSortParm"] = string.IsNullOrEmpty(sortOrder) ? "first_desc" : "";

    // 维护过滤参数
    ViewData["CurrentFilter"] = model.FirstMidName;
    ViewData["CurrentFilter2"] = model.LastName;
    ViewData["CurrentFilter3"] = model.EnrollmentDateFrom;
    ViewData["CurrentFilter4"] = model.EnrollmentDateUntil;

    // 基础查询
    var studentsQuery = from s in _context.Students select s;

    // 应用排序
    studentsQuery = sortOrder switch
    {
        "name" => studentsQuery.OrderBy(s => s.LastName),
        "name_desc" => studentsQuery.OrderByDescending(s => s.LastName),
        "first_desc" => studentsQuery.OrderByDescending(s => s.FirstMidName),
        "Date" => studentsQuery.OrderBy(s => s.EnrollmentDate),
        "date_desc" => studentsQuery.OrderByDescending(s => s.EnrollmentDate),
        _ => studentsQuery.OrderBy(s => s.FirstMidName),
    };

    // 应用过滤
    if (!string.IsNullOrEmpty(model.LastName))
    {
        studentsQuery = studentsQuery.Where(s => s.LastName.Contains(model.LastName));
    }
    if (!string.IsNullOrEmpty(model.FirstMidName))
    {
        studentsQuery = studentsQuery.Where(s => s.FirstMidName.Contains(model.FirstMidName));
    }
    if (model.EnrollmentDateFrom != null && model.EnrollmentDateUntil != null)
    {
        studentsQuery = studentsQuery.Where(s => s.EnrollmentDate >= model.EnrollmentDateFrom && s.EnrollmentDate <= model.EnrollmentDateUntil);
    }

    // 分页处理
    var totalCount = await studentsQuery.CountAsync();
    var studentList = await studentsQuery
        .Skip((page - 1) * maxRowsPerPage)
        .Take(maxRowsPerPage)
        .ToListAsync();

    // 组装ViewModel
    var viewModel = new StudentVM
    {
        currentPageIndex = page,
        pageCount = (int)Math.Ceiling((double)totalCount / maxRowsPerPage),
        StundentList = studentList,
        // 保留当前过滤参数
        FirstMidName = model.FirstMidName,
        LastName = model.LastName,
        EnrollmentDateFrom = model.EnrollmentDateFrom,
        EnrollmentDateUntil = model.EnrollmentDateUntil
    };

    return View(viewModel);
}

步骤2:修改视图,统一用Get提交请求

移除分页的Post表单,改为带参数的Get链接,确保所有操作保留筛选状态:

<!-- 替换原有的Post表单,用Get方式处理分页和排序 -->
<div>
    <table class="table table-hover">
        <thead>
            <tr class="table-light">
                <th class="table-light">
                    @Html.ActionLink("First Name", "Index", new { sortOrder = ViewBag.FirstSortParm, FirstMidName = Model.FirstMidName, LastName = Model.LastName, EnrollmentDateFrom = Model.EnrollmentDateFrom, EnrollmentDateUntil = Model.EnrollmentDateUntil, currentPageIndex = 1 })
                </th>
                <th>
                    @Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm, FirstMidName = Model.FirstMidName, LastName = Model.LastName, EnrollmentDateFrom = Model.EnrollmentDateFrom, EnrollmentDateUntil = Model.EnrollmentDateUntil, currentPageIndex = 1 })
                </th>
                <th>
                    @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, FirstMidName = Model.FirstMidName, LastName = Model.LastName, EnrollmentDateFrom = Model.EnrollmentDateFrom, EnrollmentDateUntil = Model.EnrollmentDateUntil, currentPageIndex = 1 })
                </th>
                <th>Opsi</th>
            </tr>
        </thead>
        <tbody class="opsi">
            @foreach (var item in Model.StundentList)
            {
                <tr>
                    <td>@Html.DisplayFor(modelItem => item.FirstMidName)</td>
                    <td>@Html.DisplayFor(modelItem => item.LastName)</td>
                    <td>@Html.DisplayFor(modelItem => item.EnrollmentDate)</td>
                    <td>
                        <a class="btn btn-warning text-white" asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                        <a class="btn btn-info text-white" asp-action="Details" asp-route-id="@item.ID">Details</a> |
                        <a class="btn btn-danger" asp-action="Delete" asp-route-id="@item.ID">Delete</a>
                    </td>
                </tr>
            }
        </tbody>
    </table>

    <ul class="pagination">
        @for(int i=1; i<=Model.pageCount; i++)
        {
            <li class="page-item @(i == Model.currentPageIndex ? "active" : "")">
                <a class="page-link" asp-action="Index" asp-route-currentPageIndex="@i" 
                   asp-route-sortOrder="@ViewBag.CurrentSort"
                   asp-route-FirstMidName="@Model.FirstMidName"
                   asp-route-LastName="@Model.LastName"
                   asp-route-EnrollmentDateFrom="@Model.EnrollmentDateFrom"
                   asp-route-EnrollmentDateUntil="@Model.EnrollmentDateUntil">
                    @i
                </a>
            </li>
        }
    </ul>
</div>

<!-- 修改搜索表单的action为Index -->
<form asp-action="Index" method="get">
    <div class="form-actions no-color">
        <h4>Search By</h4>
        <div class="box">
            <div class="search">
                <label asp-for="FirstMidName"></label>
                <input name="FirstMidName" type="text" value="@ViewData["CurrentFilter"]"/>
                <
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 23:45:38