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)"> « </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)"> » </a> </li> } </ul> </nav> </div> </div>
问题根源
- 前端渲染逻辑缺陷:当前代码会生成所有总页码的DOM元素,当总页数过大(比如2500条/10条每页=250页),大量
<li>元素会直接撑爆容器,导致样式错位、溢出屏幕。 - 后端查询效率不足:一次性加载整个分类下的所有子分类及 recipes 到内存再分页,数据量过大时会导致内存占用过高,甚至可能引发查询延迟,间接导致页码计算异常。
- 页码边界校验缺失:未对传入的
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)"> « </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)"> » </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
相关产品推荐
相关产品推荐

