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

ADO.NET 最佳實踐指南:資料提供者選擇、DataSet/DataReader 抉擇、效能優化與進階技巧

     編輯:關於.NET

資料提供者選擇策略

.NET 資料提供者在應用程式與資料源間扮演橋樑。為達最佳效能,**務必選用針對特定資料源優化的提供者**:

資料源推薦提供者命名空間
SQL Server 7.0+SQL Server .NET 資料提供者System.Data.SqlClient
OLE DBOLE DB .NET 資料提供者System.Data.OleDb
ODBCODBC .NET 資料提供者System.Data.Odbc
OracleOracle .NET 資料提供者System.Data.OracleClient / ODP.NET

SQL Server 提供者直接存取 TDS 協定,無額外轉換層,效能最佳。

DataSet vs DataReader:核心抉擇

使用 DataSet 的情境

  • 需在多個離散結果表間導航
  • 整合多資料源資料 (多資料庫、XML、試算表)
  • 跨層傳遞 / XML Web Service (DataReader 無法序列化)
  • 重複使用記錄集 (快取排序、搜尋、篩選)
  • 每筆記錄需大量處理 (避免長時間佔用連線)
  • 需 XML 操作 (XSLT、XPath)

使用 DataReader 的情境

  • 不需快取資料
  • 結果集過大超出記憶體
  • 需唯讀、順向、高速存取

注意: DataAdapter.Fill 內部使用 DataReader,改用 DataAdapter 節省的記憶體與迴圈開銷通常屬象徵性,決策應以功能需求為主。

強型別 DataSet 優勢

  • 設計期型別檢查
  • IntelliSense 程式碼完成
  • 以屬性存取欄位 (如 Customer.Name 而非 row["Name"])
  • 繼承自 DataSet,保留所有功能 (遠端、資料繫結)

處理 DBNull

XSD 結構描述中設定 nullValue:

  • String.Empty 替代
  • 保留 null
  • 擲出例外 (預設)

DataSet 進階操作技巧

刷新資料

// 依主鍵合並更新現有列,RowState 變 Unchanged
dataAdapter.Fill(dataSet);

// 保留本地修改同時刷新
dataAdapter.Fill(newDataTable);
dataSet.Merge(newDataTable, true); // preserveChanges = true

高效搜尋

  1. 主鍵查詢:DataTable.Rows.Find(key) (使用索引) 優於 Select()
  2. 非主鍵多次查詢:建立 DataView 設定 Sort 建立索引,使用 Find/FindRows
  3. 單次查詢:建立索引開銷大於收益,直接 Select()

DataView 建構最佳化

// 佳:建構子一次指定 Sort/Filter,建立一次索引
var view = new DataView(table, "Column > 5", "Column DESC", DataViewRowState.CurrentRows);

// 避免:建立空 View 後再設定屬性,導致多次建立索引
var view = new DataView(table);
view.Sort = "Column DESC"; // 觸發第二次索引建立

分頁查詢策略

避免 Fill 多載分頁

// 不佳:仍傳回完整結果集,浪費資源
da.Fill(ds, startRecord, maxRecords, "Table");

技巧一:WHERE + ORDER BY + TOP (需唯一識別碼)

// 下一頁
SELECT TOP 10 * FROM Customers WHERE Id > @LastId ORDER BY Id ASC

// 上一頁
SELECT TOP 10 * FROM Customers WHERE Id < @FirstId ORDER BY Id DESC

技巧二:巢狀 TOP (不需唯一識別碼)

-- 第 3 頁,頁面大小 10
SELECT TOP 10 * FROM
  (SELECT TOP 30 * FROM Customers ORDER BY Id ASC) AS T
ORDER BY Id DESC

本地快取分頁

資料不常變動時,DataSet 快取 10 頁資料,超出範圍再查詢新資料。

DataAdapter 結構描述與 CommandBuilder

MissingSchemaAction

da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
// 或預先呼叫
da.FillSchema(ds, SchemaType.Source);

包含主鍵、唯一約束、允許空值、最大長度、唯讀、自動遞增等結構描述資訊。預先指定結構描述可避免額外往返伺服器。

CommandBuilder 最佳實踐

  1. 限設計期/臨時方案使用;生產環境應顯式設定 Insert/Update/Delete 命令或使用預存程序
  2. SelectCommand 變更後需呼叫 RefreshSchema()
  3. 命令屬性為 null 時才自動產生;若要重新產生請先設為 null

批次 SQL 執行

多條命令合併單次往返 (SQL Server 用分號分隔) 可減少網路延遲:

-- 本地累積刪除命令,批次送出
DELETE FROM Orders WHERE Id = 1; DELETE FROM Orders WHERE Id = 2;

但會增加 DataSet 更新管理複雜度,建議每個 DataTable 對應一個 DataAdapter 保持簡單。

多表填充與 TableMappings

da.Fill(ds, "Customers");

第一個表命名為 "Customers",後續表自動命名 "Customers1"、"Customers2"... 需手動對應:

da.TableMappings.Add("Customers1", "Orders");

DataReader 進階技巧

  1. 存取輸出參數前**必須關閉 DataReader**
  2. 讀取完成**立即關閉** DataReader (若連線僅供此用途,連帶關閉連線)
  3. 跨層傳遞不可用 DataReader (僅限連線模式)
  4. 使用強型別存取器 (GetString, GetInt32) 避免裝箱轉型
  5. 單一連線同時間僅能開啟一個 DataReader,需多連線
  6. CommandBehavior.SequentialAccess:按需載入欄位,大幅降低 BLOB 記憶體佔用
  7. 大量未讀結果時,關閉前先 Command.Cancel() 放棄結果流

BLOB 讀取必用 SequentialAccess

var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
// 必須按順序讀取欄位,BLOB 使用 GetBytes/GetChars 分塊讀取

命令執行優化

OleDbCommand

  • 使用 ODBC CALL 語法呼叫預存程序 (CommandType.Text)
  • **必須**顯式設定參數型別、大小、精度/小數位,避免每次執行重建參數存取器

SqlCommand

  • 呼叫預存程序設定 CommandType.StoredProcedure,避免解析開銷

Prepare 方法

參數化命令重複執行時,Prepare() 通知資料源優化。SQL Server 2000+ 隱式優化無需呼叫,SQL Server 7.0 有效。

顯式指定中繼資料

避免 Fill、CommandBuilder、DeriveParameters 推斷中繼資料的效能損耗,生產環境應顯式定義。

ExecuteScalar / ExecuteNonQuery

  • 單一值查詢 (COUNT(*), SUM()) 用 ExecuteScalar,單步完成
  • 不傳回資料列的 DDL/DML 用 ExecuteNonQuery,避免建立空 DataReader

NULL 值處理

SQL 參數傳遞 DBNull

param.Value = DBNull.Value; // 而非 null/Nothing

WHERE 條件判斷 NULL

WHERE (LastName = @LastName) OR (LastName IS NULL AND @LastName IS NULL)

交易處理

ADO.NET 交易模型:**顯式關聯 Command 與 Transaction**

conn.Open();
var trans = conn.BeginTransaction();
cmd.Transaction = trans;
try {
    da.Update(ds);
    trans.Commit();
} catch {
    trans.Rollback();
} finally {
    conn.Close();
}

未關聯 Transaction 的 Command 會擲出例外。分散式交易需 .NET 2.0+ TransactionScope。

連線管理最佳實踐

  1. 連線池:SQL Server/OLE DB/ODBC 提供者隱式池化,連線字串控制行為
  2. DataAdapter 隱式開關:單次 Fill/Update 可讓其自動管理;多次操作應顯式 Open/Close
  3. 交易期間顯式開關:BeginTransaction 前 Open,Commit/Rollback 後 Close
  4. 永遠顯式關閉:using 區塊 (C#) 確保 Dispose 呼叫
  5. 避免 Finalize 中關閉:僅釋放非受控資源
  6. 避免頻繁讀取 OleDbConnection.State:觸發 OLE DB 往返,改用 StateChange 事件
// C# using 範例
using (var conn = new SqlConnection(connString))
using (var cmd = conn.CreateCommand())
{
    cmd.CommandText = "SELECT ...";
    conn.Open();
    using (var dr = cmd.ExecuteReader())
    {
        while (dr.Read()) Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1));
    }
}

XML 整合

DataSet 深度整合 XML:

  • 從 XSD 載入結構描述
  • 從 XML 載入資料 / 推斷結構描述 (僅限設計期)
  • 輸出 XSD / XML
  • XmlDataDocument 同步關聯/階層檢視,支援 XPath/XSLT

SQL Server XML 功能

  • FOR XML 查詢搭配 SqlCommand.ExecuteXmlReader
  • SQLXML Managed Classes (Microsoft.Data.SqlXml) 支援 XPath、XSLT、XML 模板

其他實用技巧

避免自動遞增衝突

  • DataSet 端設定 AutoIncrementStep = -1, AutoIncrementSeed = 0 產生負數 ID
  • 資料庫端正數遞增,雙方不衝突
  • 或改用 GUID 主鍵

開放式並行衝突檢測

  • 資料表加 Timestamp 欄位
  • 或 UPDATE WHERE 所有原始欄位值匹配

多執行緒

  • ADO.NET 物件非執行緒安全 (除 DataSet 多讀單寫需鎖定)

COM Interop 僅作最後手段

需 ADOMD 等 ADO 獨有功能時才使用,效能較差。

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