Commit()/Rollback()。Complete() 通知完成;若未 Complete 父事務會阻塞或回滾。Transaction.Current 存於 TLS,新執行緒看不到。解法:主執行緒建立 DependentTransaction 傳給背景執行緒,背景執行緒設為 Transaction.Current。
private static void Transfer(string from, string to, double amount)
{
var originalTx = Transaction.Current;
var rootTx = new CommittableTransaction();
try
{
Transaction.Current = rootTx;
ThreadPool.QueueUserWorkItem(state =>
{
var depTx = state as DependentTransaction;
Transaction.Current = depTx;
try
{
Withdraw(from, amount);
Deposit(to, amount);
depTx.Complete(); // 通知父事務此分支完成
}
catch (Exception ex)
{
Transaction.Current.Rollback(ex);
}
finally
{
(depTx as IDisposable).Dispose();
Transaction.Current = null;
}
}, rootTx.DependentClone(DependentCloneOption.BlockCommitUntilComplete));
// BlockCommitUntilComplete:Commit 時等待所有 DependentTransaction.Complete()
// 其他同步作業...
rootTx.Commit();
}
catch (TransactionAbortedException ex)
{
rootTx.Rollback(ex);
}
finally
{
Transaction.Current = originalTx;
rootTx.Dispose();
}
}
| 選項 | 行為 | 適用場景 |
|---|---|---|
BlockCommitUntilComplete | 父事務 Commit 阻塞,等待 Complete 或逾時 | **非同步分支**,必須確保完成才算數 |
RollbackIfNotComplete | 父事務 Commit 時若分支未 Complete → 直接回滾拋出 | **同步輔助作業**,失敗即整體失敗 |
目標:方法**有事務則加入,無事務則自建**。
static void InvokeInTransaction(Action action)
{
var originalTx = Transaction.Current;
CommittableTransaction rootTx = null;
DependentTransaction depTx = null;
if (Transaction.Current == null)
{
rootTx = new CommittableTransaction();
Transaction.Current = rootTx;
}
else
{
depTx = Transaction.Current.DependentClone(DependentCloneOption.RollbackIfNotComplete);
Transaction.Current = depTx;
}
try
{
action(); // 執行業務邏輯
if (rootTx != null) rootTx.Commit();
if (depTx != null) depTx.Complete();
}
catch (Exception ex)
{
Transaction.Current.Rollback(ex);
throw;
}
finally
{
var tx = Transaction.Current;
Transaction.Current = originalTx;
tx.Dispose();
}
}
// 使用:Withdraw/Deposit 變成事務型方法
static void Withdraw(string id, double amt) => InvokeInTransaction(() => Db.Execute("P_WITHDRAW", id, amt));
static void Deposit(string id, double amt) => InvokeInTransaction(() => Db.Execute("P_DEPOSIT", id, amt));
上述 InvokeInTransaction 正是 TransactionScope 內部邏輯的簡化版。**實務上 95% 場景直接用 TransactionScope**。
using (var scope = new TransactionScope())
{
Withdraw("A", 100);
Deposit("B", 100);
scope.Complete(); // 標記成功,Dispose 時 Commit
} // Dispose 時自動 Commit/Rollback、還原 Transaction.Current
| 參數 | 型別 | 說明 |
|---|---|---|
| scopeOption | TransactionScopeOption | 環境事務存在時的行為 (Required/RequiresNew/Suppress) |
| scopeTimeout | TimeSpan | 事務逾時 (預設 1 分鐘) |
| transactionOptions | TransactionOptions | IsolationLevel + Timeout 組合 |
| interopOption | EnterpriseServicesInteropOption | 與 COM+ 事務互操作模式 |
| 模式 | 行為 | 典型用途 |
|---|---|---|
Required (預設) | 有事務則加入 (建立 DependentTx),無則建新 (CommittableTx) | 一般業務邏輯 |
RequiresNew | **永遠建立新 CommittableTx**,與外部事務無關 | 獨立日誌寫入、審計追蹤、補償操作 |
Suppress | **暫時移除環境事務** (Transaction.Current = null),無事務控制 | 非關鍵通知發送、快取刷新、唯讀查詢 |
using (var outer = new TransactionScope()) // 預設 IsolationLevel.Serializable
{
var opts = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
using (var inner = new TransactionScope(TransactionScopeOption.Required, opts))
{
// ArgumentException: 衝突的隔離級別
}
}
規則:Required 內層指定的 IsolationLevel **必須等於** 外層實際級別。建議:最外層統一指定,內層不再覆寫。
| 選項 | 行為 |
|---|---|
None | 不同步 COM+ Context 與 System.Transactions |
Automatic | 偵測現有 COM+ Context 並同步 (較常用) |
Full | 強制雙向同步,效能損耗大 (可能建立新 COM+ Context) |
Complete() 只是設定內部旗標 _complete = true,**不立即 Commit**。Dispose() 時:_complete 為 true → Commit() (或 DependentTx.Complete());false → Rollback()。Complete() 後至 Dispose() 前,**無法存取 Transaction.Current** (拋 InvalidOperationException)。DependentClone(BlockCommitUntilComplete)。TransactionScopeOption.RequiresNew。Suppress 避免不必要鎖爭用。TransactionAbortedException 後繼續用同事務;讓它向上傳播觸發 Rollback。TransactionalOperation 實作 OnCommit/OnAbort。System.Transactions 提供了統一、可組合的事務抽象:CommittableTransaction 為根,DependentTransaction 為分支,TransactionScope 為語法糖。掌握三者關係與選項語意,即可在分散式系統中自信處理 ACID 事務,避免手動鎖管理的地獄。