程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF事務傳播

WCF事務傳播

編輯:關於.NET

目錄

事務流配置

事務流和操作合同

環境事務

事務性服務編程

事務傳播模式

客戶端和服務事務

表決和完成

總結

事務是構建強大可靠、高質量的面向服務的應用程序的關鍵。Windows® Communication Foundation 為服務開發人員提供了簡單的聲明性事務支持,使 您能夠完全在服務的作用域之外配置諸如事務流和表決等參數。此外,Windows Communication Foundation 還允許客戶端應用程序創建事務,並跨服務邊界傳 播它們。在本專欄中,我將詳細介紹如何在 Windows Communication Foundation 中配置事務傳播和表決,以及所產生的編程模型。

事務流配置

Windows Communication Foundation 可以跨服務邊界傳播事務。這使得服務 能夠參與客戶端的事務,並使客戶端能夠在同一事務中包含對多個服務的操作調 用。客戶端本身可能是、也可能不是 Windows Communication Foundation 服務 。綁定和操作合同配置二者都會控制客戶端事務是否傳播給服務的決策。

對於能夠將客戶端的事務傳播給服務(如果配置為這樣做)的任何綁定,我 都稱之為事務感知綁定。具體來說,只有 NetTcpBinding、 NetNamedPipeBinding、WSHttpBinding、WSDualHttpBinding 和 WSFederationHttpBinding 是事務感知綁定。默認情況下,事務感知綁定不傳播 事務。原因是,像 Windows Communication Foundation 中的其他大多數綁定一 樣,它是可選設置。

服務宿主或管理員必須顯式同意接受可能來自跨組織或跨業務邊界傳入的事 務。若要傳播事務,必須在服務宿主和客戶端的綁定上都顯式啟用它。所有事務 感知綁定都提供布爾屬性 TransactionFlow,其默認值為 False,如下所示:

public class NetTcpBinding : Binding,...
{
  public bool TransactionFlow {get;set;}
  //More members
}

若要啟用傳播,只需以編程方式或在宿主配置文件中將此屬性設置為 True。 例如,使用配置文件時此代碼片段將設置該屬性:

<bindings>
  <netTcpBinding>
   <binding name = “TransactionalTCP” transactionFlow = “true” />
  </netTcpBinding>
</bindings>

注意,服務元數據中不會發布 TransactionFlow 屬性的值。如果使用 Visual Studio® 2005 生成客戶端配置文件,則仍然需要手動啟用事務流。

事務流和操作合同

使用事務感知綁定甚至啟用事務流,並不意味著服務想在每個操作中都使用 客戶端的事務,或者客戶端首要的任務是傳播事務。這樣的決策應當是客戶端與 服務之間合同的一部分。對於這一點,Windows Communication Foundation 提 供了 TransactionFlowAttribute 方法屬性,用於控制客戶端的事務是否及何時 流入服務中:

public enum TransactionFlowOption
{
  Allowed,
  NotAllowed,
  Mandatory
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class TransactionFlowAttribute :
  Attribute,IOperationBehavior
{
  public TransactionFlowAttribute(TransactionFlowOption flowOption);
}

注意,TransactionFlow 屬性是方法級屬性,因為 Windows Communication Foundation 要求關於事務流的決策必須在每個操作級別做出:

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  [TransactionFlow(TransactionFlowOption.Allowed)]
  void MyMethod(...);
}

這樣做旨在讓一些方法使用客戶端事務,而讓一些方法不使用客戶端事務, 從而實現粒度。

TransactionFlow 屬性的值包含在服務的已發布元數據中。當導入合同定義 時,導入的定義將包含該配置後的值。這些屬性是 TransactionFlowOption.NotAllowed、TransactionFlowOption.Allowed 和 TransactionFlowOption.Mandatory。

如果通過屬性 TransactionFlowOption.NotAllowed 將操作配置為禁止事務 流,則客戶端無法將其事務傳播給服務。即使在綁定中啟用了事務流,並且客戶 端有事務,該事務流仍將被默認忽略,而不會傳播給服務。因此,服務永遠不會 使用客戶端的事務,並且服務和客戶端可以選擇具有任何配置的任何綁定。 TransactionFlowOption.NotAllowed 是 TransactionFlow 屬性的默認 TransactionFlowOption 值。

通過使用 TransactionFlowOption.Allowed 將操作配置為允許事務流時,如 果客戶端有事務,則服務將允許客戶端的事務跨服務邊界流動。但是,即使傳播 了客戶端的事務,服務也不一定會使用該事務。選擇 TransactionFlowOption.Allowed 時,可以將服務配置為使用任何綁定(事務感 知綁定或非事務感知綁定),但客戶端和服務必須在其綁定配置中實現兼容。如 果服務操作允許事務流,但綁定禁止事務流,則客戶端也應當在其綁定中禁止它 。如果嘗試使客戶端事務流動,將會導致錯誤,因為服務將無法了解消息中的事 務信息。但是,當服務器端綁定配置被設置為允許事務流時,客戶端不一定希望 在它這一端啟用傳播,可能因此選擇在綁定中將 TransactionFlow 設置為 False,即使服務已將它設置為 True。

如果配置此操作用於 TransactionFlowOption.Mandatory,則服務和客戶端 必須使用啟用了事務流的事務感知綁定。Windows Communication Foundation 將在服務加載時確認此要求,如果服務有至少一個不兼容的端點,則會引發 InvalidOperationException 異常。TransactionFlowOption.Mandatory 意味著 客戶端必須將事務傳播給服務。如果在沒有事務的情況下嘗試調用服務,則在客 戶端會引發異常。使用強制流時,客戶端的事務將始終傳播給服務。同樣,服務 不一定使用客戶端的事務。

將客戶端事務傳播給服務要求(由其性質決定)允許服務中止客戶端事務。 這意味著您無法使客戶端事務通過單向操作流向服務,因為該調用沒有獲得回復 消息。Windows Communication Foundation 將在服務加載時驗證這一點,如果 配置單向操作用於除 TransactionFlowOption.NotAllowed 以外的屬性,將引發 異常:

//Invalid definition:
[ServiceContract]
interface IMyContract
{
  [OperationContract(IsOneWay = true)]
  [TransactionFlow(TransactionFlowOption.Allowed)]
  void MyMethod(...);
}

環境事務

該 Transaction 類是在 Microsoft® .NET Framework 2.0 中引入的, 它來自 System.Transactions 命名空間,用於表示 Windows Communication Foundation 事務:

[Serializable]
public class Transaction : IDisposable,ISerializable
{
  public static Transaction Current {get;set;}
  public TransactionInformation TransactionInformation {get;}
  public void Dispose();
  //More members
}

開發人員很少需要直接與該 Transaction 類交互。.NET Framework 2.0 定 義了稱為環境事務的概念,它是指用於當前正在執行的代碼的事務。若要獲得對 環境事務的引用,請調用 Transaction 的靜態 Current 屬性:

Transaction ambientTransaction = Transaction.Current;

如果沒有環境事務,Current 將返回 null。每個代碼塊(無論是客戶端還是 服務)都始終可以訪問其環境事務。環境事務對象被存儲在線程本地存儲中。因 此,當線程穿過同一調用鏈上的多個對象和方法時,所有對象和方法都可以訪問 其環境事務。

在 Windows Communication Foundation 的上下文中,環境事務極其重要。 如果存在環境事務,任何 Windows Communication Foundation 資源管理器(例 如,SQL Server™ 或不穩定資源管理器)都將在環境事務中自動登記。當 客戶端調用 Windows Communication Foundation 服務時,如果客戶端有環境事 務和綁定,並且合同被配置為允許事務流,則環境事務將傳播給服務。

Transaction 類用於本地事務,也用於分布式事務。如果不需要分布式事務 支持,Windows Communication Foundation 將使用本地事務管理器。如果客戶 端嘗試將它的事務流向服務,或者涉及多項持久資源,則事務將被提升為分布式 事務,並由分布式事務控制 (DTC) 進行管理。每個事務有兩個標識符,用於標 識本地事務和分布式事務。通過訪問 Transaction 類的 TransactionInformation 屬性,可以獲得事務標識符。 TransactionInformation 屬於如下定義的 TransactionInformation 類型:

public class TransactionInformation
{
  public Guid DistributedIdentifier {get;}
  public string LocalIdentifier {get;}
  //More members
}

LocalIdentifier 永遠不為 null;它表示本地事務。如果事務尚未被提升為 分布式事務,則 DistributedIdentifier 可能是 Guid.Empty。這些標識符的主 要用途是用於日志記錄、跟蹤和分析。在本專欄中,為了方便,我將在代碼中使 用它們來演示作為配置結果的事務流。

事務性服務編程

對於服務來說,Windows Communication Foundation 提供了簡單而出色的聲 明性編程模型。默認情況下,服務類和它的操作沒有環境事務。即使在將客戶端 事務傳播給服務時,也是如此。

以圖 1 顯示的服務為例。服務的環境事務將是 null,即使強制事務流可以 保證客戶端的事務傳播。為了具有環境事務,對於每個合同方法,服務都必須通 過使用 OperationBehavior 屬性的 TransactionScopeRequired 屬性,表明它 希望 Windows Communication Foundation 為事務限定方法主體的作用域,如以 下代碼所示:

Figure1簡單服務合同

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  [TransactionFlow(TransactionFlowOption.Mandatory)]
  void MyMethod(...);
}
class MyService : IMyContract
{
  public void MyMethod(...)
  {
   Transaction transaction = Transaction.Current;
   Debug.Assert(transaction == null);
  }
}
  
[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationBehaviorAttribute : Attribute,...
{
  public bool TransactionScopeRequired {get;set;}
  //More members
}

TransactionScopeRequired 的默認值是 False,這就是為什麼默認情況下服 務沒有環境事務的原因。如果將 TransactionScopeRequired 設置為 True,則 可以提供具有環境事務的操作:

class MyService : IMyContract
{
  [OperationBehavior(TransactionScopeRequired = true)]
  public void MyMethod()
  {
   Transaction transaction = Transaction.Current;
   Debug.Assert(transaction != null);
  }
}

如果客戶端事務被傳播給服務,Windows Communication Foundation 會將客 戶端事務設置為操作的環境事務。否則,Windows Communication Foundation 將為該操作創建新事務,並將新事務設置為環境事務。

圖 2 中的關系圖演示了作為綁定配置、合同操作以及本地操作行為屬性的結 果,Windows Communication Foundation 服務使用了哪個事務。

在圖 2 中,非事務性客戶端調用 Service 1,操作合同使用 TransactionFlowOption.Allowed 進行配置。即使在綁定中啟用了事務流,由於 客戶端沒有事務,因此不傳播事務。Service 1 上的操作行為被配置為需要事務 作用域。因此,Windows Communication Foundation 為 Service 1 創建了新事 務 Transaction A。然後,Service 1 調用了分別具有不同配置的其他三個服務 。

圖 2事務傳播

用於 Service 2 的綁定啟用了事務流,而且由操作合同控制客戶端事務的流 動。由於操作行為被配置為需要事務作用域,因此,Windows Communication Foundation 將 Transaction A 設置為 Service 2 的環境事務。對 Service 3 的調用具有綁定,並且操作合同禁止事務流。但是,由於 Service 3 的操作行 為需要事務作用域,因此,Windows Communication Foundation 為 Service 3 創建了新事務 (Transaction B),並將它設置為 Service 3 的環境事務。與 Service 3 相似,對 Service 4 的調用具有綁定,並且操作合同禁止事務流。 由於 Service 4 不需要事務作用域,因此它沒有環境事務。

事務傳播模式

服務使用哪個事務要由綁定的流屬性(兩個值)、操作合同中的流選項(三 個值)和操作行為中的事務作用域屬性的值(兩個值)決定。因此,有 12 種可 能的配置設置。這 12 個值中,有 4 個不一致,被 Windows Communication Foundation 排除(例如,在綁定中禁用的流同時又是操作合同中的強制流), 或完全不適用。圖 3 列出了剩余的 8 種排列。

Figure3事務傳播模式

綁定事務流 事務 - FlowOption 事務 - ScopeRequired 事務模式 False 允許 False 無 False 允許 True 服務 False 不允許 False 無 False 不允許 True 服務 True 允許 False 無 True 允許 True 客戶端/服務 True 強制 False 無 True 強制 True 客戶端

這 8 種排列實際只產生四種事務傳播模式。我將這四種模式稱為客戶端/服 務模式、客戶端模式、服務模式和無模式。圖 3 還用綠色字體顯示了配置每種 模式的推薦方式。在設計應用程序時,這些模式都有各自的用處。了解如何選擇 正確的模式可以極大地簡化思考和配置事務支持的過程。

顧名思義,客戶端/服務模式用於確保服務如果可能則使用客戶端事務,或者 當客戶端沒有事務時則使用服務端事務。客戶端/服務模式是最分離的配置,因 為服務對客戶端活動的假設程度最低。如果客戶端有要流動的事務,則服務將加 入客戶端事務。加入客戶端事務總是對系統總體一致性有利。假設服務的某個事 務與客戶端的某個事務分離:這將使兩個事務中的某一個有可能在另一個中止時 被提交,從而使系統處於不一致狀態。

當服務加入客戶端事務後,由客戶端和服務執行的所有工作(並且可能包括 客戶端調用的其他服務)將作為一個不可分割的操作被提交或中止。如果客戶端 沒有事務,服務仍然需要保護事務,這樣,通過使服務成為新事務的根,該模式 向服務提供了臨時事務。成為根後,就可以使服務控制事務的生命期。圖 4 顯 示了采用客戶端/服務事務配置的服務。注意,服務斷言它始終有事務。服務無 法假設或斷言它是客戶端的事務,還是本地創建的事務。

Figure4 配置客戶端/服務模式

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  [TransactionFlow(TransactionFlowOption.Allowed)]
  void MyMethod(...);
}
class MyService : IMyContract
{
  [OperationBehavior(TransactionScopeRequired = true)]
  public void MyMethod(...)
  {
   Transaction transaction = Transaction.Current;
   Debug.Assert(transaction != null);
  }
}

當服務可以獨立使用,或者它可以用作更大事務的組成部分時,則適用客戶 端/服務模式。選擇此模式時,應當留心可能出現的死鎖現象。具體來說,如果 所得到的事務是服務端事務,則在其他事務嘗試訪問相同資源時,它可能發生死 鎖,因為資源會按事務隔絕訪問,服務端事務將成為新事務。使用客戶端/服務 模式時,服務不一定會成為事務的根,如果服務成為根或加入客戶端的事務,則 服務的行動一定不能有變化。

客戶端和服務事務

客戶端模式可確保服務只使用客戶端的事務。如果根據設計服務必須使用它 的客戶端的事務,並且永遠不能單獨使用服務時,則應當選擇客戶端事務模式。 這樣做的主要目的是為了避免死鎖,並最大程度實現系統總體一致性。通過讓服 務共享客戶端的事務,可以減少出現死鎖的可能性,因為被訪問的所有資源都將 登記在同一事務中,因此,不會有其他事務來爭奪對相同資源和基本鎖定的訪問 。通過擁有單個事務,可以最大程度確保一致性,因為事務將作為一個不可分割 的操作而提交或中止。圖 5 顯示了采用客戶端事務模式配置的服務。

Figure5配置客戶端事務模式

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  [TransactionFlow(TransactionFlowOption.Mandatory)]
  void MyMethod(...);
}
class MyService : IMyContract
{
  [OperationBehavior(TransactionScopeRequired = true)]
  public void MyMethod(...)
  {
   Transaction transaction = Transaction.Current;
   Debug.Assert(transaction.TransactionInformation.
          DistributedIdentifier != Guid.Empty);
  }
}

注意,該方法斷言環境事務是分布式事務,這意味著事務是由客戶端發起的 。服務模式可確保服務始終具有事務,它與其客戶端具有或不具有的任何事務分 離。服務將始終作為新事務的根。

如果服務需要在客戶端事務的作用域以外執行事務性工作,則應當選擇服務 事務模式。例如,如果要執行某些日志記錄或審核操作,或者要向訂閱者發布事 件,而不考慮客戶端事務是否提交或中止。假設有一個要將錯誤記錄到數據庫中 的日志簿服務。如果客戶端發生錯誤,則客戶端會使用日志簿服務來記錄錯誤或 某些其他條目。萬一發生錯誤,則在記錄完成後,客戶端的錯誤將中止客戶端的 事務。如果服務要使用客戶端事務,則一旦客戶端事務中止後,所記錄的錯誤就 會從數據庫中刪除,並且無法跟蹤它,從而破壞了將日志記錄擺在首要位置的目 的。

通過將服務配置為擁有它自己的事務,則即使客戶端事務中止,錯誤也會被 記錄。當然,缺點是有可能損害系統一致性,因為當客戶端執行提交時,服務事 務可能中止。通過選擇此模式所獲得的啟發是:與客戶端的事務相比,服務事務 更有可能成功並提交。在日志記錄服務示例中,通常情況就是這樣的,因為一旦 有明確的記錄,相對於可能由於多種原因而導致失敗的業務事務,通常這種模式 可以很好地發揮作用。一般來說,使用服務事務模式時應當非常小心,並且應當 確認在一個事務中止而另一個提交的情況下兩個事務(客戶端的事務和服務的事 務)不會損害一致性。日志記錄和審核服務是此模式的典型候選方案。

圖 6 中的代碼顯示了采用服務事務模式配置的服務。應當注意,服務斷言它 實際上有本地事務。

Figure6配置服務事務模式

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  void MyMethod(...);
}
class MyService : IMyContract
{
  [OperationBehavior(TransactionScopeRequired = true)]
  public void MyMethod(...)
  {
   Transaction transaction = Transaction.Current;
   Debug.Assert(transaction.TransactionInformation.
          DistributedIdentifier == Guid.Empty);
  }
}

無事務模式意味著服務永遠沒有事務。如果服務執行的操作最好擁有(雖然 不是必需的),並且如果操作失敗不應當中止客戶端的事務,則此模式很有用。 例如,為資金劃轉打印回執的服務在打印機缺紙時不應中止客戶端事務。使用無 模式的另一個原因是在您想提供某些自定義行為時,您需要執行自己的程序事務 支持或手動登記諸如調用舊事務代碼這樣的資源。

很明顯,使用無模式存在危險,因為它可能損害系統一致性。例如,如果發 出調用的客戶端有事務,並且它調用了采用無模式配置的服務,然後客戶端中止 了它的事務,那麼,結果是服務對系統狀態的更改將不會回滾。當采用無模式配 置的服務調用另一個采用客戶端事務模式配置的服務時,會暴露出此模式的另一 個缺陷。此類調用將失敗,因為發出調用的服務沒有要傳播的事務。

圖 7 顯示了采用無事務模式配置的服務。注意,服務斷言它沒有環境事務。

Figure7配置無事務模式

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  void MyMethod();
}
class MyService : IMyContract
{
  public void MyMethod()
  {
   Transaction transaction = Transaction.Current;
   Debug.Assert(transaction == null);
  }
}

無模式允許您有被事務客戶端調用的非事務性服務。正如前面所述,采用無 模式配置在大多數情況下針對的是最好擁有的操作。這樣導致的問題是,無模式 服務所引發的任何異常都將使發出調用的客戶端的事務中止,而這是最好擁有的 操作應當避免的。解決方案是讓客戶端捕獲來自無模式服務的所有相關異常,以 避免干擾客戶端的事務。

在四種模式中,服務模式和無模式有點深奧。它們在上述所提到的特定情形 中很實用,但請記住,它們都存在著損害系統一致性的危險。您應當使用客戶端 /服務或客戶端事務模式,並根據可能的死鎖和一致性對服務單獨使用能力的影 響來選擇模式。

表決和完成

盡管 Windows Communication Foundation 負責事務傳播的各個方面以及跨 資源提交或中止事務的整體管理工作,但它並不知道事務應當提交還是中止。 Windows Communication Foundation 無法知道對系統狀態的更改是否是一致的 ,就是說,它們是否有意義。每個參與事務的服務都必須對事務應當提交還是中 止進行表決。此外,Windows Communication Foundation 不知道事務何時結束 ,以及所有服務何時完成其工作。這就是根服務需要指示的內容。

Windows Communication Foundation 可以自動代表服務對提交或中止事務進 行表決。自動表決由 OperationBehavior 屬性的 TransactionAutoComplete 布 爾屬性進行控制:

[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationBehaviorAttribute : Attribute,...
{
  public bool TransactionAutoComplete {get;set;}
  //More members
}

TransactionAutoComplete 屬性默認值為 True。當設置為 True 時,如果操 作中沒有未處理的異常,Windows Communication Foundation 將自動表決提交 事務。如果有未處理的異常,Windows Communication Foundation 將表決中止 事務。只有包含會話的服務才能將 TransactionAutoComplete 設置為 False, 並且這樣做對編程模型有強烈的暗示,這超出了本專欄的討論范圍。

事務何時結束由它的啟動者決定。假設一個客戶端沒有事務或者沒有將它的 事務傳播給服務,並且該客戶端調用了 TransactionScopeRequired 被設置為 true 的服務操作。這樣,該服務操作就成為事務的根。根服務可以調用其他服 務,並將事務傳播給它們。一旦根操作完成事務,事務就將結束。這就是為什麼 TransactionAutoComplete 如此命名的部分原因。它的作用不僅僅是表決;它還 可以完成和終止根服務的事務。請注意,被根操作所調用的任何下游服務都只能 對事務表決,而不能完成該事務。只有根才能表決並完成事務。使用事務作用域 實際上是非服務客戶端可以將多個服務調用組合成單個事務的唯一方式,如圖 8 所示。

圖 8使用事務作用域

當非服務客戶端啟動一個事務時,事務將在該客戶端釋放它所使用的事務對 象時結束(通常是釋放 TransactionScope 對象時)。創建根事務作用域的做法 使客戶端能夠將它的事務流向服務,並能根據服務的聚合結果來管理和提交事務 :

using(MyContractClient proxy1 = new MyContractClient())
using(MyOtherContractClient proxy2 = new MyOtherContractClient())
using(TransactionScope scope = new TransactionScope())
{
  proxy1.MyMethod(...);
  proxy2.MyOtherMethod(...);
  scope.Complete();
}

總結

事務在很多應用程序中是很重要的,它可以確保系統在發生錯誤後順利恢復 。能夠跨技術和服務邊界將來自多個供應商的多個服務組合成單個不可分割的事 務是 Windows Communication Foundation 以及它支持的標准的關鍵功能。而且 ,開發人員所要做的只是通過設置少量屬性並在綁定中啟用流,以選擇合適的邏 輯事務傳播模式。這使得 Windows Communication Foundation 成為簡單而功能 強大的編程模型。Windows Communication Foundation 中的事務支持意義不止 如此,尤其是在同步和實例管理方面,我將在以後的專欄中對這些方面進行介紹 。

有關事務和 Windows Communication Foundation 的詳細信息,請參閱 Introducing System.Transactions in the .NET Framework 2.0 和《MSDN 雜 志》在 2005 年 12 月刊載的 Volatile Resource Managers in .NET Bring Transactions to the Common Type。

將您想詢問的問題和提出的意見發送至: [email protected]@microsoft.com.

Juval Lowy是 IDesign 的一名提供 WCF 培訓和 WCF 體系結構咨詢的軟件架 構師。他最近編寫了一本名為《Programming WCF Services》(WCF 服務編程技 術) (O’Reilly, 2007) 的新書。另外,他還是 Microsoft 硅谷地區的 區域總監。您可以通過 www.idesign.net 與 Juval 聯系。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved