首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# ASP 風格分頁顯示:手寫分頁邏輯與現代替代方案

     編輯:關於C語言

前言

早期 ASP 開發者習慣在頁面直接撰寫分頁邏輯。ASP.NET WebForms 雖有 `DataGrid`/`GridView` 內建分頁,但理解底層分頁算法對自訂查詢、大數據優化、API 分頁仍至關重要。本文補全原始碼片段,展示完整手寫分頁實作,並對比現代 ORM/LINQ 分頁寫法。

核心變數定義


private int _pageSize = 20;      // 每頁筆數
private int _pageCount;          // 總頁數
private int _recordCount;        // 總筆數
private int _currentPage = 1;    // 當前頁碼 (1-based)

完整分頁實作


public void BindData()
{
    // 1. 取得總筆數
    string countSql = "SELECT COUNT(*) FROM Articles WHERE IsPublished=1";
    _recordCount = Convert.ToInt32(SqlHelper.ExecuteScalar(countSql));
    
    // 2. 計算總頁數
    _pageCount = (_recordCount + _pageSize - 1) / _pageSize;
    if (_currentPage > _pageCount) _currentPage = _pageCount;
    if (_currentPage < 1) _currentPage = 1;

    // 3. 建構分頁 SQL (SQL Server 2012+ OFFSET/FETCH)
    string sql = @"
        SELECT Id, Title, CreateTime, Summary 
        FROM Articles 
        WHERE IsPublished=1 
        ORDER BY CreateTime DESC
        OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";
    
    var parameters = new[]
    {
        new SqlParameter("@Offset", (_currentPage - 1) * _pageSize),
        new SqlParameter("@PageSize", _pageSize)
    };
    
    DataTable dt = SqlHelper.ExecuteDataTable(sql, parameters);
    gridView.DataSource = dt;
    gridView.DataBind();

    // 4. 產生分頁導覽 HTML
    litPager.Text = BuildPagerHtml();
}

private string BuildPagerHtml()
{
    var sb = new StringBuilder();
    sb.Append("
"); // 首頁/上一頁 if (_currentPage > 1) { sb.Append($"首頁 "); sb.Append($"上一頁 "); } // 頁碼區塊 (顯示前後各 2 頁) int start = Math.Max(1, _currentPage - 2); int end = Math.Min(_pageCount, _currentPage + 2); for (int i = start; i <= end; i++) { if (i == _currentPage) sb.Append($"{i} "); else sb.Append($"{i} "); } // 下一頁/末頁 if (_currentPage < _pageCount) { sb.Append($"下一頁 "); sb.Append($"末頁 "); } sb.Append($"共 {_recordCount} 筆 / 第 {_currentPage}/{_pageCount} 頁"); sb.Append("
"); return sb.ToString(); } // 頁面載入時還原頁碼 protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { _currentPage = Request.QueryString["page"] != null ? int.Parse(Request.QueryString["page"]) : 1; BindData(); } }

資料庫分頁語法對比

資料庫語法適用版本
SQL Server`OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY`2012+
SQL Server (舊)`ROW_NUMBER() OVER (ORDER BY ...) AS RN` + 外層 `WHERE RN BETWEEN ...`2005-2008 R2
MySQL / PostgreSQL / SQLite`LIMIT @PageSize OFFSET @Offset`所有版本
Oracle`OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY`12c+
Oracle (舊)`ROWNUM` 子查詢雙層包裹11g及之前

現代 ORM/LINQ 分頁一鍵搞定

Entity Framework Core


var query = _context.Articles.Where(a => a.IsPublished)
    .OrderByDescending(a => a.CreateTime);

int total = await query.CountAsync();
var items = await query.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();

// 回傳 DTO
return new PagedResult
{
    Items = items.Select(MapToDto).ToList(),
    TotalCount = total,
    Page = page,
    PageSize = pageSize
};

Dapper


string sql = @"SELECT * FROM Articles WHERE IsPublished=1 ORDER BY CreateTime DESC 
               OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";
var items = await conn.QueryAsync
(sql, new { Offset = (page-1)*pageSize, PageSize = pageSize }); int total = await conn.ExecuteScalarAsync("SELECT COUNT(*) FROM Articles WHERE IsPublished=1");

通用分頁回傳模型


public class PagedResult
{
    public List Items { get; set; }
    public int TotalCount { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
    public int TotalPages => (TotalCount + PageSize - 1) / PageSize;
    public bool HasPrev => Page > 1;
    public bool HasNext => Page < TotalPages;
}

大數據量優化關鍵

  1. 避免 `SELECT *`:只查詢列表需要的欄位。
  2. 索引支援 ORDER BY:`CREATE INDEX IX_Articles_Published_Time ON Articles(IsPublished, CreateTime DESC)`。
  3. 游標分頁:`WHERE Id < @LastId ORDER BY Id DESC LIMIT @PageSize`,適合無限滾動,避免 `OFFSET` 深分頁效能劣化。
  4. 快取總筆數:總數變化低頻時,Redis 快取 `Count` 減少查詢。

總結

手寫分頁核心在於 **`COUNT(*)` 取總數 + `OFFSET/FETCH` 取分頁資料 + 頁碼邊界檢查 + 導覽 HTML 生成**。現代開發請直接使用 **EF Core `Skip/Take` 或 Dapper 原生 SQL**,搭配統一 `PagedResult` 模型,專注於索引優化與游標分頁策略,而非重複造輪子。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved