DataGrid 內建分頁仍會一次撈回所有資料,效能不彰。本文設計 IPaginationManager 介面,支援隨機跳頁、指定頁面大小、頁面快取,真正實現「只撈當頁資料」。
public interface IPaginationManager
{
void Initialize(DataPaginationParas paras);
void Initialize(IDBAccesser accesser, int pageSize, string whereStr, string[] fields);
DataTable GetPage(int index); // 取得指定頁 (0-based)
DataTable CurrentPage(); // 目前頁
DataTable PrePage(); // 上一頁
DataTable NextPage(); // 下一頁
int PageCount { get; } // 總頁數
int CacherSize { get; set; } // 快取頁數 (<=0 不快取, MaxValue 無限)
}
public class DataPaginationParas
{
public int PageSize = 10;
public string[] Fields = { "*" };
public string ConnectString;
public string TableName;
public string WhereStr;
public DataPaginationParas(string connStr, string tableName, string whereStr)
{
ConnectString = connStr; TableName = tableName; WhereStr = whereStr;
}
public string GetFieldString()
{
if (Fields == null) Fields = new[] { "*" };
return string.Join(", ", Fields);
}
}
public DataTable GetPage(int index)
{
// 檢查快取
if (cache.Contains(index)) return cache[index];
// 組裝分頁 SQL (採用 ROW_NUMBER() OVER 頁碼法,SQL 2005+)
string sql = $@"
WITH Paged AS (
SELECT {paras.GetFieldString()},
ROW_NUMBER() OVER (ORDER BY {GetOrderBy()}) AS RowNum
FROM {paras.TableName}
WHERE {paras.WhereStr}
)
SELECT * FROM Paged
WHERE RowNum BETWEEN @Start AND @End";
int start = index * paras.PageSize + 1;
int end = (index + 1) * paras.PageSize;
DataTable dt = dbAccesser.ExecuteDataTable(sql,
new SqlParameter("@Start", start),
new SqlParameter("@End", end));
// 更新快取 (LRU)
if (CacherSize > 0)
{
cache[index] = dt;
if (cache.Count > CacherSize) cache.RemoveOldest();
}
return dt;
}
GetPage(index) 直接取得任意頁,不需循序讀取。Initialize 供代碼生成器直接注入 IDBAccesser。var paras = new DataPaginationParas(connStr, "Orders", "Status=1");
var mgr = PaginationFactory.CreateManager(); // 實作 IPaginationManager
mgr.Initialize(paras);
mgr.CacherSize = 20;
DataTable page1 = mgr.GetPage(0); // 第 1 頁
DataTable page3 = mgr.GetPage(2); // 直接跳第 3 頁