原子性要求交易內所有操作要麼全做,要麼全不做。若中途發生錯誤,必須執行回滾抹除已執行效果,維護資料庫一致性。
try {
// 1. 開啟連線、啟動交易
// 2. 執行多條 DML
// 3. 人為製造錯誤:CREATE DATABASE 測試回滾
// 4. 提交交易
tran.Commit();
} catch (Exception ex) {
tran.Rollback(); // 完整回滾
Console.WriteLine("交易錯誤,已回滾:" + ex.Message);
}
using System;
using System.Data;
using System.Data.SqlClient;
namespace ASPcn {
public class DbTran {
public void DoTran() {
using (SqlConnection conn = GetConn()) {
conn.Open();
SqlCommand cmd = conn.CreateCommand();
SqlTransaction tran = conn.BeginTransaction();
cmd.Transaction = tran;
try {
cmd.CommandText = "USE pubs";
cmd.ExecuteNonQuery();
// 正常更新
cmd.CommandText = "UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'PC%'";
cmd.ExecuteNonQuery();
// 人為製造錯誤:在交易中建立資料庫(需權限,且會導致交易中止)
cmd.CommandText = "CREATE DATABASE testdb";
cmd.ExecuteNonQuery();
// 後續更新不會執行
cmd.CommandText = "UPDATE roysched SET royalty = royalty * 1.20 WHERE title_id LIKE 'PS%'";
cmd.ExecuteNonQuery();
tran.Commit();
} catch (Exception ex) {
tran.Rollback();
Console.WriteLine("交易錯誤,已回滾:" + ex.Message);
}
}
}
private SqlConnection GetConn() {
string cs = "Data Source=localhost;Integrated Security=SSPI;User ID=sa;Password=";
return new SqlConnection(cs);
}
}
public class Test {
public static void Main() {
new DbTran().DoTran();
Console.WriteLine("交易處理完成。");
Console.ReadLine();
}
}
}
UPDATE (royalty * 1.10) 雖已執行,但因後續 CREATE DATABASE 失敗觸發 Rollback(),資料庫狀態回到交易前,無任何變更。Rollback() 確實維護了原子性與一致性。| 錯誤寫法 | 問題 | 修正 |
|---|---|---|
| catch 區塊未呼叫 Rollback | 交易懸掛,鎖不釋放,連線池洩漏 | catch 必須呼叫 Rollback() |
| using 區塊包裹 SqlConnection 但未在 catch Rollback | Dispose 時隱式 Rollback,但例外資訊遺失 | 顯式 Rollback 後再拋出或記錄 |
| 多 catch 區塊重複 Rollback | 第二次 Rollback 擲回 InvalidOperationException | 統一在單一 catch 或 finally 處理 |