事務(Transaction):一組SQL操作採用原子執行,要麼全部成功提交(Commit),要麼全部失敗回滾(Rollback),符合ACID特性。
在.NET Framework/.NET 當中透過 SqlTransaction(僅適用SQL Server)來實作。
流程重點:
- 開啟資料庫連線
conn.Open()- 透過連線建立事務
conn.BeginTransaction()- 將事務物件指派給
SqlCommand.Transaction- 批次執行多筆SQL敘述
- 無例外:
tx.Commit();捕捉例外:tx.Rollback()
範例
原始程式碼缺少catch回滾處理邏輯,這裡提供完整可執行版本:
public static int ExecuteSqlTran(List<string> SQLStringList)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
SqlTransaction tx = conn.BeginTransaction();
cmd.Transaction = tx;
try
{
int count = 0;
for (int n = 0; n < SQLStringList.Count; n++)
{
string strsql = SQLStringList[n];
if (strsql.Trim().Length > 1)
{
cmd.CommandText = strsql;
count += cmd.ExecuteNonQuery();
}
}
tx.Commit(); // 全部執行成功,提交事務
return count;
}
catch (Exception ex)
{
tx.Rollback(); // 任一指令出錯,全部回滾
throw ex; // 向外拋出例外,讓上層知道執行失敗
}
}
}Code language: C# (cs)
此版本直接拼接SQL字串,存在SQL注入風險,適合拿來理解事務原理;正式專案務必優先使用參數化查詢
加入參數化的事務範例
迴圈寫入資料,透過SqlParameter防範注入,幾個關鍵注意點:
- 每次迴圈結束呼叫
sqlCom.Parameters.Clear()清除舊參數,避免參數累積導致報錯 - 重複使用同一個
SqlCommand物件,只更新CommandText與參數內容
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = conn;
SqlTransaction st = conn.BeginTransaction();
sqlCom.Transaction = st;
int res = 0;
try
{
for (int i = 0; i < 10; i++)
{
sqlCom.CommandText = "INSERT INTO [dbo].[Article]([Title])VALUES(@Title)";
SqlParameter param = new SqlParameter("@Title", SqlDbType.NVarChar, 250);
param.Value = i + "這是新的標題" + DateTime.Now;
sqlCom.Parameters.Add(param);
res += sqlCom.ExecuteNonQuery();
sqlCom.Parameters.Clear(); // 清空參數,供下一輪使用
}
st.Commit();
}
catch (Exception)
{
st.Rollback(); // 發生例外,全部復原
}Code language: C# (cs)
1. 一定要遵守的強制規則
BeginTransaction()必須在連線Open之後才呼叫SqlCommand一定要設定Transaction屬性,否則會拋出例外- 呼叫完
Commit()/Rollback()事務就自動結束,不可重複呼叫
其他備註
- .NET Core/.NET 5+ 建議使用
using自動釋放SqlTransaction - 處理大量SQL批次場景,可以考慮
SqlBulkCopy,效能比迴圈呼叫ExecuteNonQuery更好 - 跨資料庫的事務需求,SqlTransaction無法支援,要改用分散式事務(
TransactionScope)
SqlTransaction 資料庫事務
Previous: ExecuteNonQuery
Next: SqlDataReader