ASP.NET Core 8 MVC分页调用存储过程时页码异常问题求助
问题解决与最佳实践建议
核心问题:第二次点击Next页码变回1的原因及修复
1. 路由与HTTP方法不匹配
你的Next控制器方法未添加[HttpPost]特性,但表单是以POST方式提交的,导致点击Next按钮时无法匹配到对应动作方法,最终默认路由到Search动作,模型绑定失败时CurrentPageNumber会使用默认值1。
修复: 给所有分页相关动作方法添加[HttpPost]特性:
[HttpPost] public async Task<IActionResult> Next(SearchViewModel svm) { if (svm.CurrentPageNumber < svm.TotalPageCount) svm.CurrentPageNumber += 1; return await Search(svm); } // 同理给Back、First、Last方法添加[HttpPost] [HttpPost] public async Task<IActionResult> Back(SearchViewModel svm) { if (svm.CurrentPageNumber > 1) svm.CurrentPageNumber -= 1; return await Search(svm); }
2. 表单标签笔误
表单标签存在拼写错误:asp-asp-controller应改为asp-controller,否则表单默认路由失效,影响按钮动作匹配:
<!-- 修复前 --> <form id="frmSearch" method="post" asp-asp-controller="Home" asp-action="Search"> <!-- 修复后 --> <form id="frmSearch" method="post" asp-controller="Home" asp-action="Search">
3. 确保分页参数正确传递
检查SearchViewModel中CurrentPageNumber等字段是否设置了公共get/set属性,同时确保返回视图时传递的svm已更新所有分页参数。你当前使用@Html.HiddenFor的方式是正确的,但要保证视图渲染时隐藏字段输出的是最新值。
分页实现的最佳实践建议
1. 无敏感条件时优先使用GET请求处理分页
如果搜索条件不含敏感信息,建议将分页操作改为GET请求,页码、每页条数等参数可放在URL中,支持书签、刷新,同时避免表单隐藏字段的维护问题:
控制器修改:
[HttpGet] public async Task<IActionResult> Search(SearchViewModel svm) { try { var pageSize = svm.PageSize <= 0 ? 20 : svm.PageSize; var pageNumber = svm.CurrentPageNumber <= 0 ? 1 : svm.CurrentPageNumber; var parameters = new[] { new SqlParameter("@PageNumber", pageNumber), new SqlParameter("@PageSize", pageSize) }; List<CommunicationSearchResult> communications = await _recordingContext.CommunicationSearchResults.FromSqlRaw("EXEC USP_DETAILED_SEARCH_COMMUNICATIONS @PageNumber, @PageSize", parameters).ToListAsync(); svm.Results = communications; if (communications.Count > 0) { svm.TotalRecordCount = communications[0].Total; svm.TotalPageCount = Convert.ToInt32(Math.Ceiling((decimal)svm.TotalRecordCount / pageSize)); } return View(svm); } catch(Exception ex) { // 异常处理逻辑 return View(svm); } }
视图分页按钮修改(使用a标签):
<a asp-action="Search" asp-route-currentPageNumber="1" asp-route-pageSize="@Model.PageSize" class="btn btn-secondary">First</a> <a asp-action="Search" asp-route-currentPageNumber="@(Model.CurrentPageNumber - 1)" asp-route-pageSize="@Model.PageSize" class="btn btn-secondary" @(Model.CurrentPageNumber <=1 ? "disabled" : "")>Back</a> <a asp-action="Search" asp-route-currentPageNumber="@(Model.CurrentPageNumber + 1)" asp-route-pageSize="@Model.PageSize" class="btn btn-secondary" @(Model.CurrentPageNumber >= Model.TotalPageCount ? "disabled" : "")>Next</a> <a asp-action="Search" asp-route-currentPageNumber="@Model.TotalPageCount" asp-route-pageSize="@Model.PageSize" class="btn btn-secondary">Last</a>
2. 封装分页逻辑
将分页参数处理、存储过程调用等逻辑封装到服务类中,减少控制器代码冗余:
public class CommunicationSearchService { private readonly RecordingContext _context; public CommunicationSearchService(RecordingContext context) { _context = context; } public async Task<(List<CommunicationSearchResult> Results, int TotalCount)> Search(int pageNumber, int pageSize) { pageNumber = pageNumber <= 0 ? 1 : pageNumber; pageSize = pageSize <= 0 ? 20 : pageSize; var parameters = new[] { new SqlParameter("@PageNumber", pageNumber), new SqlParameter("@PageSize", pageSize) }; var results = await _context.CommunicationSearchResults.FromSqlRaw("EXEC USP_DETAILED_SEARCH_COMMUNICATIONS @PageNumber, @PageSize", parameters).ToListAsync(); var totalCount = results.Count > 0 ? results[0].Total : 0; return (results, totalCount); } }
控制器中注入使用:
private readonly CommunicationSearchService _searchService; public HomeController(CommunicationSearchService searchService) { _searchService = searchService; } [HttpPost] public async Task<IActionResult> Search(SearchViewModel svm) { try { var (results, totalCount) = await _searchService.Search(svm.CurrentPageNumber, svm.PageSize); svm.Results = results; svm.TotalRecordCount = totalCount; svm.TotalPageCount = Convert.ToInt32(Math.Ceiling((decimal)totalCount / svm.PageSize)); return View(svm); } catch(Exception ex) { // 异常处理 return View(svm); } }
3. 添加边界值校验
在分页动作中添加边界值校验,避免页码超出总页数或小于1:
[HttpPost] public async Task<IActionResult> Next(SearchViewModel svm) { svm.CurrentPageNumber = Math.Min(svm.CurrentPageNumber + 1, svm.TotalPageCount); return await Search(svm); } [HttpPost] public async Task<IActionResult> Back(SearchViewModel svm) { svm.CurrentPageNumber = Math.Max(svm.CurrentPageNumber - 1, 1); return await Search(svm); }
4. 统一分页参数来源
确保Search动作中使用svm.PageSize而非未定义的pagesize变量,避免分页大小不一致:
// 替换原代码中的pagesize变量 var pageSize = svm.PageSize <= 0 ? 20 : svm.PageSize;
内容的提问来源于stack exchange,提问作者John Geoghan
相关产品推荐
相关产品推荐

