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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:54:51