ASP.NET大数据分页怎样实现?高效分页方案详解
- 技术教程
- 2026-02-07
- 3536
在ASP.NET中处理大数据分页时,为了提高性能,应避免一次性加载所有数据,而是采用数据库级别的分页,以下是详细实现方案:
核心方案:数据库分页(SQL Server为例)
使用 OFFSET FETCH 或 ROW_NUMBER() 实现高效分页:

ASP.NET Web Forms 实现步骤
存储过程实现分页
CREATE PROCEDURE GetPagedData @PageIndex INT, @PageSize INT AS BEGIN SELECT * FROM BigDataTable ORDER BY ID OFFSET (@PageIndex - 1) * @PageSize ROWS FETCH NEXT @PageSize ROWS ONLY; END
C# 数据访问层
public DataTable GetPagedData(int pageIndex, int pageSize) { using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand("GetPagedData", conn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddWithValue("@PageIndex", pageIndex); cmd.Parameters.AddWithValue("@PageSize", pageSize); SqlDataAdapter da = new SqlDataAdapter(cmd); DataTable dt = new DataTable(); da.Fill(dt); return dt; } }
页面逻辑(.aspx.cs)
private int CurrentPage { get => ViewState["CurrentPage"] as int? ?? 1; set => ViewState["CurrentPage"] = value; } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) BindData(); } private void BindData() { DataTable dt = GetPagedData(CurrentPage, 10); // 每页10条 GridView1.DataSource = dt; GridView1.DataBind(); // 绑定分页控件(需自定义) BindPager(); } // 分页按钮事件 protected void btnNext_Click(object sender, EventArgs e) { CurrentPage++; BindData(); }
高效分页控件(替代GridView自带分页)
<!-- ASPX页面 --> <asp:Repeater ID="rptData" runat="server"> <ItemTemplate> <%# Eval("ColumnName") %> </ItemTemplate> </asp:Repeater> <div class="pager"> <asp:Button ID="btnPrev" runat="server" Text="上一页" OnClick="btnPrev_Click" /> <span>页码: <%= CurrentPage %></span> <asp:Button ID="btnNext" runat="server" Text="下一页" OnClick="btnNext_Click" /> </div>
ASP.NET Core MVC 实现方案
分页模型类
public class PagedResult<T> { public List<T> Items { get; set; } public int TotalCount { get; set; } public int PageIndex { get; set; } public int PageSize { get; set; } }
使用Dapper执行分页查询
public PagedResult<Product> GetProducts(int pageIndex, int pageSize) { var sql = @" SELECT COUNT(*) FROM Products; SELECT * FROM Products ORDER BY Id OFFSET (@PageIndex - 1) * @PageSize ROWS FETCH NEXT @PageSize ROWS ONLY;"; using (var multi = connection.QueryMultiple(sql, new { pageIndex, pageSize })) { return new PagedResult<Product> { TotalCount = multi.ReadSingle<int>(), Items = multi.Read<Product>().ToList(), PageIndex = pageIndex, PageSize = pageSize }; } }
Controller 实现
public IActionResult Index(int page = 1, int pageSize = 20) { var model = _repository.GetProducts(page, pageSize); return View(model); }
View 分页显示
@model PagedResult<Product> @foreach (var item in Model.Items) { <div>@item.Name</div> } <!-- 分页导航 --> <div> @if (Model.PageIndex > 1) { <a asp-action="Index" asp-route-page="@(Model.PageIndex - 1)">上一页</a> } <span>@Model.PageIndex / @(Model.TotalCount / Model.PageSize + 1)</span> @if (Model.PageIndex * Model.PageSize < Model.TotalCount) { <a asp-action="Index" asp-route-page="@(Model.PageIndex + 1)">下一页</a> } </div>
性能优化关键点
-
索引优化:
- 确保 ORDER BY 和 WHERE 涉及的列有索引
- 覆盖索引减少IO:SELECT Column1, Column2 → 创建 (Column1, Column2) 索引
-
**避免SELECT ***:

-- 只查询必要字段 SELECT Id, Name, CreatedDate FROM Table
-
参数化查询:

- 防止SQL载入
- 复用执行计划
-
前端优化:
- 异步加载:使用AJAX分页 // jQuery示例 $("#btnNext").click(function() { $.get("/Controller/PartialView?page=" + nextPage, function(html) { $("#container").html(html); }); });
-
百万级数据优化:
-- Keyset分页(游标分页) SELECT TOP (@PageSize) * FROM Table WHERE Id > @LastId ORDER BY Id
- 优先数据库分页:使用OFFSET FETCH或等效语法
- 严格索引优化:分页字段必须建立索引
- 避免全表扫描:只查询必要字段
- 前端异步加载:AJAX分页减少整页刷新
- 百万级数据:考虑Keyset分页方案
- 参数化查询:防止SQL载入攻破
不同数据库分页语法
数据库 分页查询语法 SQL Server OFFSET @offset ROWS FETCH NEXT @limit ROWS MySQL LIMIT @offset, @limit PostgreSQL LIMIT @limit OFFSET @offset Oracle OFFSET @offset ROWS FETCH NEXT @limit ROWS 扩展方案:Entity Framework Core
// 分页扩展方法 public static async Task<PagedResult<T>> ToPagedResultAsync<T>( this IQueryable<T> query, int pageIndex, int pageSize) { return new PagedResult<T> { TotalCount = await query.CountAsync(), Items = await query.Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToListAsync(), PageIndex = pageIndex, PageSize = pageSize }; } // 使用示例 var pagedData = await dbContext.Products .OrderBy(p => p.Id) .ToPagedResultAsync(pageIndex, pageSize);
重要提示:EF Core的Skip().Take()在SQL Server 2012+会转换为OFFSET FETCH,但需确保排序字段有索引。
通过以上方案,可有效处理千万级数据分页,页面响应时间控制在毫秒级,实际部署时需结合查询执行计划分析和压力测试。