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

ASP.NET Core 8 MVC分页异常求助:大数据量下显示异常

ASP.NET Core 8分页异常问题排查与解决

问题现象

当分类中的记录数少于2500条时,分页可正常显示页码1、2、3、4等;但记录数超过2500条时,分页显示异常,出现溢出屏幕、CSS样式错位、页码不从1开始的情况,且无论每页设置10、25还是50条记录,该问题都会出现。

现有代码

后端C#代码

public async Task<IActionResult> Category(int id, int page = 1)
{
    var category = await _context.Categories
                                 .Include(c => c.Subcategories)
                                 .ThenInclude(sc => sc.Recipes)
                                 .FirstOrDefaultAsync(c => c.CategoryId == id);
 
    if (category == null)
    {
        return NotFound();
    }

    var subcategories = category.Subcategories
                                .Select(sc => sc.SubcategoryName)
                                .Distinct()
                                .ToList();

    var recipes = category.Subcategories
                          .SelectMany(sc => sc.Recipes)
                          .Skip((page - 1) * PageSize)
                          .Take(PageSize)
                          .ToList();

    int totalRecipes = category.Subcategories
                               .SelectMany(sc => sc.Recipes)
                               .Count();

    ViewBag.CategoryID = id;
    ViewBag.CategoryName = category.CategoryName;
    ViewBag.Subcategories = subcategories;
    ViewBag.CurrentPage = page;
    ViewBag.TotalPages = (int)Math.Ceiling((double)totalRecipes / PageSize);

    return View(recipes);
}

前端分页控件代码

<!-- Pagination Controls -->
<div class="row justify-content-center mt-4">
    <div class="col-12 text-center">
        <nav aria-label="Page navigation">
            <ul class="pagination pagination-lg">
               @if (ViewBag.CurrentPage > 1)
               {
                   <li class="page-item">
                       <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@(ViewBag.CurrentPage - 1)">
                       &laquo;
                       </a>
                   </li>
               }

               @for (int i = 1; i <= ViewBag.TotalPages; i++)
               {
                   <li class="page-item @(i == ViewBag.CurrentPage ? "active" : "")">
                       <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@i">@i</a>
                   </li>
               }

               @if (ViewBag.CurrentPage < ViewBag.TotalPages)
               {
                   <li class="page-item">
                       <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@(ViewBag.CurrentPage + 1)">
                       &raquo;
                       </a>
                   </li>
               }
           </ul>
       </nav>
   </div>
</div>

问题根源

  1. 前端渲染逻辑缺陷:当前代码会生成所有总页码的DOM元素,当总页数过大(比如2500条/10条每页=250页),大量<li>元素会直接撑爆容器,导致样式错位、溢出屏幕。
  2. 后端查询效率不足:一次性加载整个分类下的所有子分类及 recipes 到内存再分页,数据量过大时会导致内存占用过高,甚至可能引发查询延迟,间接导致页码计算异常。
  3. 页码边界校验缺失:未对传入的page参数做有效性校验,若用户手动输入超出总页数的页码,会导致页码显示逻辑混乱。

解决方案

一、优化前端分页显示逻辑(核心解决样式问题)

不再渲染所有页码,只显示当前页前后固定数量的页码,搭配首页、末页跳转按钮,同时添加溢出处理:

<!-- 优化后的分页控件 -->
<div class="row justify-content-center mt-4 pagination-container">
    <div class="col-12 text-center">
        <nav aria-label="Page navigation">
            <ul class="pagination pagination-lg">
                <!-- 首页按钮 -->
                @if (ViewBag.CurrentPage > 1)
                {
                    <li class="page-item">
                        <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="1">首页</a>
                    </li>
                }

                <!-- 上一页按钮 -->
                @if (ViewBag.CurrentPage > 1)
                {
                    <li class="page-item">
                        <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@(ViewBag.CurrentPage - 1)">
                            &laquo;
                        </a>
                    </li>
                }

                <!-- 显示当前页前后3个页码 -->
                @{
                    int startPage = Math.Max(1, ViewBag.CurrentPage - 3);
                    int endPage = Math.Min(ViewBag.TotalPages, ViewBag.CurrentPage + 3);
                }

                @if (startPage > 1)
                {
                    <li class="page-item disabled"><span class="page-link">...</span></li>
                }

                @for (int i = startPage; i <= endPage; i++)
                {
                    <li class="page-item @(i == ViewBag.CurrentPage ? "active" : "")">
                        <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@i">@i</a>
                    </li>
                }

                @if (endPage < ViewBag.TotalPages)
                {
                    <li class="page-item disabled"><span class="page-link">...</span></li>
                }

                <!-- 下一页按钮 -->
                @if (ViewBag.CurrentPage < ViewBag.TotalPages)
                {
                    <li class="page-item">
                        <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@(ViewBag.CurrentPage + 1)">
                            &raquo;
                        </a>
                    </li>
                }

                <!-- 末页按钮 -->
                @if (ViewBag.CurrentPage < ViewBag.TotalPages)
                {
                    <li class="page-item">
                        <a class="page-link" asp-action="Category" asp-route-id="@ViewBag.CategoryID" asp-route-page="@ViewBag.TotalPages">末页</a>
                    </li>
                }
            </ul>
        </nav>
    </div>
</div>

添加CSS处理溢出:

.pagination-container {
    overflow-x: auto;
}

二、优化后端查询逻辑(提升性能+修复页码异常)

直接在数据库层面分页查询,避免加载所有数据到内存,同时添加页码边界校验:

public async Task<IActionResult> Category(int id, int page = 1)
{
    // 校验页码有效性
    if (page < 1)
    {
        page = 1;
    }

    var category = await _context.Categories
                                 .Include(c => c.Subcategories)
                                 .FirstOrDefaultAsync(c => c.CategoryId == id);
 
    if (category == null)
    {
        return NotFound();
    }

    var subcategories = category.Subcategories
                                .Select(sc => sc.SubcategoryName)
                                .Distinct()
                                .ToList();

    // 直接在数据库层面查询分页数据
    var recipesQuery = _context.Recipes
                               .Where(r => r.Subcategory.CategoryId == id);

    int totalRecipes = await recipesQuery.CountAsync();
    int totalPages = totalRecipes == 0 ? 1 : (int)Math.Ceiling((double)totalRecipes / PageSize);

    // 确保当前页不超过总页数
    if (page > totalPages)
    {
        page = totalPages;
    }

    var recipes = await recipesQuery
                          .Skip((page - 1) * PageSize)
                          .Take(PageSize)
                          .ToListAsync();

    ViewBag.CategoryID = id;
    ViewBag.CategoryName = category.CategoryName;
    ViewBag.Subcategories = subcategories;
    ViewBag.CurrentPage = page;
    ViewBag.TotalPages = totalPages;

    return View(recipes);
}

三、额外校验点

  • 确认PageSize为常量且未被意外修改,保证分页计算逻辑一致。
  • 检查前端路由参数page的传递是否正常,避免参数丢失或类型转换错误。

内容的提问来源于stack exchange,提问作者Dan Nick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:14:51