WCF在預定義綁定中實現了標準的WSAtomicTranscation(WS-AT)協議和Microsoft專有的OleTx協議,這些協議可以用來在訊息中加入事務狀態的資訊。我們可以指定將一個操作的代碼放在事務範圍裡執行。
我們需要在Binding的配置節裡指定,transcationFlow=true:
<bindings> <wsHttpBinding> <binding name="TranscationBindingConfig" transactionFlow="true"> <security mode="None" /> </binding> </wsHttpBinding></bindings>
注意:任何被指定的必須在一個事務範圍裡執行的操作(OperationContract)都不能標記為單向方法(IsOneWay=true),因為在操作結束時,有關事務狀態的資訊必須傳回給調用者。開發人員也可以指出,當沒有異常發生時,WCF應該自動為操作提交事務。
namespace WcfTranServiceLib{ [ServiceContract] public interface IDAOService { [OperationContract(IsOneWay=false)] [TransactionFlow(TransactionFlowOption.Mandatory)] void InsAndUpdProduct(Products product); [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] void InsAndUpdSupplier(Suppliers supplier); [OperationContract] List<Products> GetProducts(string productID); [OperationContract] List<Suppliers> GetSuppliers(string supplierID); }}
如果需要在服務實現的操作需要自己來提交事務,可以使用WCF的靜態System.ServiceModel.OperationContext對象:
public class MyServiceType{ [OperationBehavior(TranscationScopeRequire=true, TrancationAutoComplete=false)] void MyMethod() { // 其他的商務邏輯 OperationContext.Current.SetTranscationComplete(); }}
而用戶端開發人員可以使用System.Transcation命名空間提供的文法將服務的操作放進一個事務範圍內。
using(TranscationScope ts = new TranscationScope(TranscationScopeOption.RequireNew)){ client.DoSomething(); ... ts.Complete();}client.Close();
如果服務的操作支援事務,而且也配置了支援傳送有關事務狀態的綁定,那麼用戶端的事務直到服務作業決定提交時才會提交(TranscationScope的Complete被調用)。相反地,服務在用戶端事務範圍裡對事務資源所做的任何操作都將被復原。(用戶端處理的過程中出現任何異常)
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Transactions;namespace WcfTranClient{ class Program { static void Main(string[] args) { try { var client = new DaoSvc.DAOServiceClient(); using (var ts = new TransactionScope()) { DaoSvc.Suppliers s = new DaoSvc.Suppliers(); s.SupplierID = "S02"; s.CompanyName = "CompanyName1"; s.Address = "Address"; s.Phone = "123456"; client.InsAndUpdSupplier(s); DaoSvc.Products p = new DaoSvc.Products(); p.ProductID = "P02"; p.ProductName = "ProductName1"; p.UnitPrice = 5; p.SupplierID = "S02"; client.InsAndUpdProduct(p); //throw new Exception("Test Exception"); ts.Complete(); } var ps = client.GetProducts(null); Console.WriteLine("Count of products: {0}", ps.Count); var ss = client.GetSuppliers(null); Console.WriteLine("Count of suppliers: {0}", ss.Count); client.Close(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { Console.Read(); } } }}