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

ASP.NET LINQ 分頁與傳統分頁完整實作範例

     編輯:ASP.NET 基礎

前言

分頁是資料呈現的核心功能。本文提供兩種完整實作:LINQ to SQL 分頁 (適合 ORM 場景) 與 PagedDataSource 傳統分頁 (適合 DataTable/DataSet),並包含頁碼列生成邏輯,可直接套用於專案中。

一、LINQ 分頁核心邏輯

public class LinqPager
{
    private readonly MyDataContext _dc = new MyDataContext();
    
    /// 
    /// 通用 LINQ 分頁方法,回傳 HTML 頁碼列字串
    /// 
    public string GetPageHtml<T>(GridView gridView, int pageSize, IQueryable<T> query)
    {
        // 1. 取得當前頁碼 (QueryString)
        int page = 1;
        if (HttpContext.Current.Request.QueryString["page"] != null)
            page = Convert.ToInt32(HttpContext.Current.Request.QueryString["page"]);
        
        // 2. 總筆數 (執行 Count)
        int totalCount = query.Count();
        
        // 3. 分頁查詢 (Skip + Take) - 關鍵:延遲執行,只查當頁資料
        var pageData = query.Skip((page - 1) * pageSize).Take(pageSize);
        
        // 4. 繫結 GridView
        gridView.DataSource = pageData;
        gridView.DataBind();
        
        // 5. 生成頁碼列 HTML
        return GeneratePagerHtml(page, pageSize, totalCount);
    }
    
    /// 
    /// 生成分頁 HTML (共用邏輯)
    /// 
    private string GeneratePagerHtml(int currentPage, int pageSize, int totalCount)
    {
        int totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
        if (totalPages == 0) totalPages = 1;
        
        // 修正頁碼範圍
        currentPage = Math.Max(1, Math.Min(currentPage, totalPages));
        
        // 計算顯示頁碼範圍 (以當前頁為中心,最多顯示 10 頁)
        int startPage = Math.Max(1, currentPage - 4);
        int endPage = Math.Min(totalPages, startPage + 9);
        if (endPage - startPage < 9) startPage = Math.Max(1, endPage - 9); // 確保滿 10 頁
        
        var sb = new StringBuilder();
        sb.Append($"共 {totalPages} 頁 / {totalCount} 筆 ");
        
        // 首頁、上一頁
        if (currentPage > 1)
        {
            sb.Append($"首頁 ");
            sb.Append($"上一頁 ");
        }
        else
        {
            sb.Append("首頁 ");
            sb.Append("上一頁 ");
        }
        
        // 數字頁碼
        for (int i = startPage; i <= endPage; i++)
        {
            if (i == currentPage)
                sb.Append($"{i} ");
            else
                sb.Append($"{i} ");
        }
        
        // 下一頁、末頁
        if (currentPage < totalPages)
        {
            sb.Append($"下一頁 ");
            sb.Append($"末頁");
        }
        else
        {
            sb.Append("下一頁 ");
            sb.Append("末頁");
        }
        
        return sb.ToString();
    }
}

二、使用範例

public partial class ProductList : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            BindData();
        }
    }
    
    private void BindData()
    {
        var pager = new LinqPager();
        var query = from p in pager._dc.Products
                    where p.IsActive == true
                    orderby p.CreateDate descending
                    select p;
        
        string pagerHtml = pager.GetPageHtml(gvProducts, 20, query);
        litPager.Text = pagerHtml; // 頁面置放 
    }
}

三、傳統 PagedDataSource 分頁 (DataTable 適用)

public static string GetPageHtml(DataTable dt, DataList dataList, int pageSize)
{
    // 1. 取得頁碼
    int page = 1;
    if (HttpContext.Current.Request.QueryString["page"] != null)
        page = Convert.ToInt32(HttpContext.Current.Request.QueryString["page"]);
    
    // 2. PagedDataSource 設定
    var pds = new PagedDataSource
    {
        DataSource = dt.DefaultView,
        AllowPaging = true,
        PageSize = pageSize,
        CurrentPageIndex = page - 1
    };
    
    // 3. 繫結
    dataList.DataSource = pds;
    dataList.DataBind();
    
    // 4. 生成頁碼 (共用上述 GeneratePagerHtml 邏輯)
    int totalCount = dt.Rows.Count;
    return GeneratePagerHtml(page, pageSize, totalCount);
}

// GeneratePagerHtml 方法同上,略

四、進階:封裝為可重用控制項

[ToolboxData("<{0}:LinqPager runat=server>")]
public class LinqPagerControl : WebControl
{
    [Bindable(true)]
    public int PageSize { get; set; } = 20;
    
    [Bindable(true)]
    public string CssClass { get; set; } = "pagination";
    
    public IQueryable DataSource { get; set; }
    
    public GridView TargetGridView { get; set; }
    
    protected override void Render(HtmlTextWriter writer)
    {
        if (DataSource == null || TargetGridView == null) return;
        
        // 執行分頁邏輯 (同上)
        // ...
        
        // 渲染分頁 UI
        writer.AddAttribute(HtmlTextWriterAttribute.Class, CssClass);
        writer.RenderBeginTag(HtmlTextWriterTag.Nav);
        writer.Write(pagerHtml);
        writer.RenderEndTag();
    }
}

五、效能優化關鍵點

場景優化策略
大量資料 (10萬+)使用 ROW_NUMBER() OVER (ORDER BY ...) 視窗函數的 SQL 分頁,避免 Skip/Take 在資料庫端產生低效查詢計畫
頻繁查詢加入索引於 `ORDER BY` 欄位;考慮快取總筆數 (Count) 短時間 (如 30 秒)
複雜查詢將 `Count()` 與分頁查詢分離,避免重複執行複雜 WHERE/JOIN
前端體驗使用 AJAX 部分更新 (UpdatePanel 或 jQuery AJAX) 避免整頁重載

六、現代化替代:ASP.NET Core / EF Core 分頁

// ASP.NET Core Controller
public async Task<IActionResult> Index(int page = 1, int pageSize = 20)
{
    var query = _context.Products.Where(p => p.IsActive).OrderByDescending(p => p.CreateDate);
    var totalCount = await query.CountAsync();
    var items = await query.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
    
    ViewBag.Pager = new PagerViewModel(page, pageSize, totalCount);
    return View(items);
}

// PagerViewModel 封裝頁碼生成邏輯
public class PagerViewModel
{
    public int CurrentPage { get; }
    public int TotalPages { get; }
    public int TotalCount { get; }
    // ... 生成 HTML 方法
}

總結

LINQ 分頁核心在於 Skip() + Take() 的延遲執行特性,配合 `Count()` 取得總筆數。傳統 `PagedDataSource` 適合 DataTable 場景。關鍵在於:只查當頁資料、快取總筆數、頁碼 UI 與資料邏輯分離。新專案建議直接使用 EF Core + Pager ViewModel 模式。

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