單一資料庫交易易處理 (ADO.NET Transaction),但**跨多個資料庫** (或異構資料來源) 的原子性操作,需分散式交易協調器 (DTC/MSDTC)。COM+ (Enterprise Services) 提供宣告式分散式交易支援。
ServicedComponent,標記 [Transaction(TransactionOption.Required)]。[assembly: ApplicationActivation(ActivationOption.Server)] (啟動為 COM+ 應用程式)。regsvcs.exe Assembly.dll 註冊至 COM+ 目錄。
using System;
using System.EnterpriseServices;
using System.Data.SqlClient;
using System.Reflection;
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationName("DistributedTxDemo")]
[assembly: Description("跨資料庫分散式交易示範")]
namespace ComPlusTxDemo
{
[Transaction(TransactionOption.Required)] // 必須在交易內執行
public class OrderProcessor : ServicedComponent
{
private const string ConnStr1 = "Server=.;Database=PubDB;User=sa;Password=...;";
private const string ConnStr2 = "Server=.;Database=Northwind;User=sa;Password=...;";
public void PlaceOrder(string customerId, string productId, int qty)
{
try
{
// DB 1: 扣庫存
ExecuteNonQuery(ConnStr1,
"UPDATE Inventory SET Qty = Qty - @qty WHERE ProductId = @pid",
("@qty", qty), ("@pid", productId));
// DB 2: 建訂單 (故意寫錯欄位名稱模擬失敗)
ExecuteNonQuery(ConnStr2,
"INSERT INTO Orders (CustomerId, ProductId, Qty, BadColumn) VALUES (@cid, @pid, @qty, 1)",
("@cid", customerId), ("@pid", productId), ("@qty", qty));
// 兩步皆成功,交易自動 Commit
ContextUtil.SetComplete(); // 明確標記完成 (非必要,正常返回即 Commit)
}
catch (Exception ex)
{
// 任何例外,標記 Abort → 所有資料庫 Rollback
ContextUtil.SetAbort();
throw new EnterpriseServicesException("分散式交易失敗,已回滾", ex);
}
}
private void ExecuteNonQuery(string connStr, string sql, params (string name, object value)[] parameters)
{
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand(sql, conn))
{
foreach (var (name, value) in parameters)
cmd.Parameters.AddWithValue(name, value);
conn.Open();
cmd.ExecuteNonQuery();
}
}
}
}
// 開發人員命令提示字元
sn -k KeyPair.snc
// 專案屬性 → 簽署 → 勾選「為組件簽署」 → 選擇 KeyPair.snc
// 或 AssemblyInfo.cs: [assembly: AssemblyKeyFile(@"..\..\KeyPair.snc")]
// 管理員權限命令提示字元
regsvcs ComPlusTxDemo.dll
// 成功會在「元件服務」→「COM+ 應用程式」看到 DistributedTxDemo
using ComPlusTxDemo;
var processor = new OrderProcessor();
try
{
processor.PlaceOrder("CUST001", "PROD001", 5);
Console.WriteLine("訂單建立成功,跨庫交易 Commit");
}
catch (Exception ex)
{
Console.WriteLine($"失敗:{ex.Message}");
}
| 項目 | 說明 |
|---|---|
| TransactionOption.Required | 呼叫者有交易則加入,無則建立新交易 |
| ApplicationActivation.Server | 在獨立 dllhost.exe 進程執行,隔離度高、支援遠端呼叫 |
| ContextUtil.SetAbort() | 標記交易失敗,觸發所有登記資源管理器 Rollback |
| ContextUtil.SetComplete() | 標記成功 (正常返回同效),提早釋放資源 |
| SqlConnection 登記 | ADO.NET SqlConnection 自動偵測 COM+ 交易上下文並登記 DTC |
using System.Transactions;
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var c1 = new SqlConnection(connStr1))
using (var c2 = new SqlConnection(connStr2))
{
c1.Open(); c2.Open(); // 自動登記 DTC (若連線字串 Enlist=true)
// ... 執行 SQL ...
}
scope.Complete(); // Commit
} // Dispose 時若未 Complete → Rollback
TransactionScope ( .NET 2.0+ ) 取代 COM+ 成為**慣用分散式交易寫法**,無需強名稱/註冊/COM+ 部署,程式碼更簡潔。但需確保 MSDTC 服務啟用、防火牆開放 135/動態埠、連線字串 Enlist=true (預設)。