通用分頁預存程序雖方便,但因在程序內部拼接動態 SQL,極易遭 SQL 注入。單純過濾單引號(轉為雙引號)無法抵禦編碼繞過攻擊,參數化查詢才是根本解方。然而傳統通用分頁預存程序無法參數化,若為每個查詢單獨寫分頁程序又會大增維護成本。
在應用程式層(C#)建立 PagerQuery 類別,封裝分頁參數(頁碼、頁面大小、主鍵、SELECT/FROM/WHERE/GROUP BY/ORDER BY 子句),由程式碼組裝成 參數化的 CTE 分頁查詢,再交由 ADO.NET 以參數方式執行,兼顧安全與重用。
public class PagerQuery
{
private int _pageIndex;
private int _pageSize = 20;
private string _pk;
private string _fromClause;
private string _groupClause;
private string _selectClause;
private string _sortClause;
private StringBuilder _whereClause = new StringBuilder();
// 屬性封裝各子句...
public string PK { get; set; }
public string SelectClause { get; set; }
public string FromClause { get; set; }
public StringBuilder WhereClause { get; set; }
public string GroupClause { get; set; }
public string SortClause { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
// 產生快取 Key
public override string GetCacheKey() { ... }
// 產生總筆數查詢
public string GenerateCountSql() { ... }
// 產生含總筆數的分頁查詢(使用 CTE + ROW_NUMBER)
public string GenerateSqlIncludeTotalRecords() { ... }
// 產生純分頁查詢
public override string GenerateSql() { ... }
}
WhereClause.Append(" AND Col = @Param") 方式建立參數化條件,參數值再由 SqlParameter 集合傳遞。TOP 巢狀查詢。var pager = new PagerQuery
{
PageIndex = 2,
PageSize = 20,
PK = "OrderID",
SelectClause = "OrderID, CustomerID, OrderDate",
FromClause = "dbo.Orders",
SortClause = "OrderDate DESC"
};
pager.WhereClause.Append(" AND CustomerID = @CustID");
// 後續建立 SqlCommand,指定 CommandText = pager.GenerateSql()
// 並加入 @CustID 參數執行