| 方法 | 回傳 | 用途 | 典型 SQL |
| ExecuteNonQuery() | int (影響列數) | INSERT、UPDATE、DELETE、DDL、無結果集預存程序 | UPDATE Products SET Price=10 WHERE ID=1 |
| ExecuteReader() | SqlDataReader | SELECT、TableDirect、有結果集預存程序 (前向唯讀) | SELECT * FROM Products |
| ExecuteScalar() | object (第一欄第一列) | 單一值查詢 (COUNT、MAX、IDENTITY、SCOPE_IDENTITY) | SELECT COUNT(*) FROM Products |
| ExecuteXmlReader() | XmlReader | FOR XML 查詢,僅 SqlCommand 支援 | SELECT * FROM Products FOR XML AUTO |
| Cancel() | void | 取消正在執行的命令 (非同步/長時間查詢) | — |
建立 SqlCommand 三種方式
// 1. 無參數建構子,後續設定屬性
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM Products";
cmd.Connection = conn;
// 2. 指定 CommandText
SqlCommand cmd = new SqlCommand("SELECT * FROM Products", conn);
// 3. 指定 CommandText 與 Connection
SqlCommand cmd = new SqlCommand("SELECT * FROM Products", conn);
// 4. 從 Connection 建立 (較少用)
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT * FROM Products";
參數化查詢:防 SQL Injection 的金鑰
string sql = "INSERT INTO Products (ProductName, UnitPrice, Discontinued) VALUES (@Name, @Price, @Disc)";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 40).Value = productName;
cmd.Parameters.Add("@Price", SqlDbType.Money).Value = unitPrice;
cmd.Parameters.Add("@Disc", SqlDbType.Bit).Value = discontinued;
// 或使用 AddWithValue (型別推斷,效能稍差)
// cmd.Parameters.AddWithValue("@Name", productName);
conn.Open();
int affected = cmd.ExecuteNonQuery();
}
優點:型別安全、自動處理引號/日期格式、計畫快取重用、防注入。
預存程序呼叫
using (SqlCommand cmd = new SqlCommand("usp_GetProductsByCategory", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@CategoryID", SqlDbType.Int).Value = categoryId;
// 輸出參數
SqlParameter retParam = cmd.Parameters.Add("@RowCount", SqlDbType.Int);
retParam.Direction = ParameterDirection.ReturnValue;
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read()) { /* 讀取資料 */ }
}
int rowCount = (int)retParam.Value;
}
非同步執行 (ASP.NET 2.0+ / .NET 4.5+ Task 化)
// APM 模式 (2.0)
cmd.BeginExecuteReader(ar => {
SqlDataReader dr = cmd.EndExecuteReader(ar);
// 處理資料
}, null);
// Task 模式 (4.5+ 推薦)
SqlDataReader dr = await cmd.ExecuteReaderAsync();
完整範例:新增產品並取回 Identity
public int InsertProduct(string name, decimal price, bool discontinued)
{
const string sql = @"
INSERT INTO Products (ProductName, UnitPrice, Discontinued)
VALUES (@Name, @Price, @Disc);
SELECT CAST(SCOPE_IDENTITY() AS INT);";
using (SqlConnection conn = new SqlConnection(_connStr))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 40).Value = name;
cmd.Parameters.Add("@Price", SqlDbType.Money).Value = price;
cmd.Parameters.Add("@Disc", SqlDbType.Bit).Value = discontinued;
conn.Open();
return (int)cmd.ExecuteScalar(); // 取回 SCOPE_IDENTITY()
}
}
效能與最佳實務
- 參數化:永遠使用參數,絕不字串拼接 SQL。
- CommandType.StoredProcedure:複雜邏輯封裝於 SP,減少網路往返、利用執行計畫快取。
- using 區塊:確保連線、命令、讀取器正確釋放。
- 避免 SELECT *:明確列出欄位,減少傳輸、利用覆蓋索引。
- 設定 CommandTimeout:長跑報表查詢適當調長 (如 120 秒)。
- 非同步:高併發 Web 應用採用
ExecuteReaderAsync 釋放執行緒。
常見錯誤與解決
| 錯誤 | 原因 | 修正 |
| SqlException: 無效的物件名稱 | 資料表/預存程序名稱拼寫錯誤、或未指定 Schema (dbo.) | 使用雙部分名稱 dbo.Table、檢查資料庫上下文 |
| ExecuteReader 要求開啟的連線 | 未呼叫 conn.Open()、或連線已關閉 | 確保執行前連線狀態為 Open |
| 參數數量不符 | SQL 參數標記與 Parameters 集合數量/名稱不符 | 仔細核對 @Name 大小寫、數量 |
| 資料截斷 | 參數 Size 過小、或資料型別不符 | 設定足夠 Size (NVarChar 4000)、確認 SqlDbType |
結語
SqlCommand 是 ADO.NET 執行資料庫操作的核心物件。掌握五大執行方法、參數化查詢、預存程序呼叫、非同步模式與最佳實務,即能寫出安全、高效、可維護的資料存取程式碼,奠定 BLL/DAL 堅實基礎。
- 上一篇文章: ASP.NET 2.0 伺服器控制項捕獲回傳事件:IPostBackEventHandler 完整實作與事件屬性結構
- 下一篇文章: ASP.NET 2.0 行動開發入門:StyleSheet 控制項與樣式管理完整指南
Copyright ©
程式碼之家 All Rights Reserved