程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Enterprise Library深入解析與靈活應用(8):通過WCF擴展實現與EHAB的集成[

Enterprise Library深入解析與靈活應用(8):通過WCF擴展實現與EHAB的集成[

編輯:關於.NET

Enterprise Library深入解析與靈活應用(8):通過WCF擴展實現與EHAB的集成[上篇]

在《WCF技術剖析(卷1)》的最後一章,我給出了一個具體的應用WCF的分布式應用實例,我把這個實例命名為PetShop。在這個例子中,我利用WCF的擴展實 現了一些設計、架構模式,比如AOP、IoC等。看過本書的讀者,一定還記得我還 通過WCF擴展實現了於微軟企業庫(Enterprise Library)異常處理應用塊 (Exception Handling Application Block:EHAB)的集成。當時由於缺乏相應 的背景知識,不可能介紹具體的實現,現在我們可以詳細來講述這是如何實現的 。

一、基本原理介紹

在一個基於WCF的分布式應用中,服務端和 客戶端需要進行單獨的異常處理。在服務端,讓EHAB處理拋出的異常是很容易的 ,我們只需要按照上面代碼所示的方式調用ExcpetionPolicy的HandleException 方法,傳入拋出的異常並指定相應的異常處理策略名稱即可。關鍵的是如何實現 讓 EHAB處理客戶端進行服務調用拋出的異常。

我們知道,客戶端進行服 務調用拋出的異常類型總是FaultException(包括 FaultException<TDetail>)。而EHAB采用的是完全基於異常類型的異常, 即拋出的異常類型決定了異常處理方式。也就是說,即使兩種完全不同的出錯場 景,只要拋出的異常具有相同的類型,EHAB就會采用相同的方式來處理該異常。 采用這樣的方式來直接處理調用WCF服務拋出的異常,顯然具有很大的局限:如果 服務不錯任何處理,客戶端捕獲的永遠是 FaultException(不包括 FaultException<TDetail>)異常,如果采用EHAB的話,意味著只有一種唯 一異常處理方式。當然,在服務端的操作實現中你可以根據具體的場景拋出 FaultException<TDetail>異常,並通過不同類型的錯誤明細(TDetail) 封裝具體的錯誤信息,那麼客戶端就可以針對具體的 FaultException<TDetail>異常類型選擇不同的方式進行處理。這樣使你的 異常處理方式是真正的場景驅動的。

理論上來講,我們需要的正是這種方 式的異常處理方式。但是在快速開發中,這樣的方式不太具有可操作性,因為異 常的一個本質屬性就是具有不可預測性。對於某項服務操作,不太可能羅列出所 有的錯誤場景並拋出相應類型的異常。這也正是我們需要一種像EHAB這樣一種可 配置的異常處理框架的原因,這樣我們才能夠通過修改相應的配置為某個我們之 前沒有預知的異常定義相應的異常處理策略。

我們接下來的介紹的解決方 案通過一種變通的方式解決了上面的問題,這種方式與通過 ServiceDebugBehavior實現異常的傳播有點類似:服務端拋出的異常先通過EHAB 按照配置好的異常處理策略進行相應的處理。然後將處理後的異常相關的信息( 包括異常類型的 AssemblyQualifiedName)封裝到一個類似於ExceptionDetail的 可序列化對象中。最後,以該對象為基礎創建 MessageFault,並進一步生成 Fault消息傳回客戶端;客戶端在接收到該Fault消息後,提取服務端異常相關的 信息利用反射重建異常對象(已經明確了異常類型的AssemblyQualifiedName使異 常對象的重建變成可能)比將其拋出。那麼對於客戶端的應用程序來說,就像是 捕獲從服務端拋出的異常一樣了。通過EHAB針對客戶端配置的異常處理策略對拋 出的異常進行處理,那麼這種異常處理方式依然是場景驅動的。

在本例中 ,我們通過如下一個名稱為ServiceExceptionDetail的類型來封裝異常相關信息 。為了簡單起見,我直接讓 ServiceExceptionDetail繼承自ExceptionDetail。 由於ServiceExceptionDetail對象需要從服務端向客戶端傳遞,我將其定義成數 據契約。在ServiceExceptionDetail僅僅定義了一個唯一的屬性成員: AssemblyQualifiedName,表示異常的類型的程序集有效名稱,這是為了基於反射 的異常重建的需要。在 ServiceExceptionDetail中,定義了3個字符串常量表示 對應SOAP Fault的SubCode名稱和命名空間,以及對應Fault消息的Action。整個 解決方法實現的原理大體上可以通過圖1示。

1: using  System;
2: using System.Runtime.Serialization;
3: using System.ServiceModel;
4: namespace  Artech.EnterLibIntegration.WcfExtensions
5: {
6:      [DataContract(Namespace = "http://www.artech.com/")]
7:     public class ServiceExceptionDetail :  ExceptionDetail
8:     {
9:          public const string FaultSubCodeNamespace =  "http://www.artech.com/exceptionhandling/";
10:          public const string FaultSubCodeName = "ServiceError";
11:         public const string FaultAction =  "http://www.artech.com/fault";
12:
13:          [DataMember]
14:         public string  AssemblyQualifiedName
15:         { get; private  set; }
16:
17:         [DataMember]
18:         public new ServiceExceptionDetail  InnerException
19:         { get; private set; }
20:
21:         public  ServiceExceptionDetail(Exception ex)
22:              : base(ex)
23:         {
24:              this.AssemblyQualifiedName = ex.GetType ().AssemblyQualifiedName;
25:             if  (null != ex.InnerException)
26:              {
27:                 this.InnerException =  new ServiceExceptionDetail(ex.InnerException);
28:              }
29:         }
30:
31:         public override string ToString()
32:         {
33:             return  this.Message;
34:         }
35:     }
36: }

圖1 WCF與EHAB集成實現原理

注:有人會覺得這和開啟了 IncludeExceptionDetailInFaults開關的 ServiceDebugBehavior服務行為一樣, 異常信息會完全暴露給客戶端,會存在敏感信息洩露的危險。實際上,如果你將 敏感信息屏蔽的操作定義在相關的異常處理策略中,並通過EHAB來實現,那麼最 終傳遞給客戶端的信息已經是經過處理的了。

二、異常處理、封裝與重建

從上面給出的整個解決方案實現原理介紹中,我們可以看出,這個結構體 系需要解決如下三個功能:

通過EHAB處理服務端拋出的原始異常 (XxxException):利用EHAB針對預定義的異常處理策略對服務操作拋出的異常 進行處理;

通過MessageFault封裝EHAB處理後的異常(YyyException): 創建ServiceExceptionDetail對象封裝通過EHAB處理後的異常,進而創建 MessageFault對象,最終創建Fault消息將異常相關的信息向客戶端傳遞;

客戶端實現異常的重建(YyyException):客戶端接收到Fault消息後,提 取異常相關信息,重建異常對象,使得客戶端可以利用EHAB針對基於客戶端的異 常處理策略對其進行相應的處理。

在本例中,我們通過兩個重要的WCF組 件實現對以上3個功能的實現,其中前兩個通過自定義的ErrorHandler實現,最後 一個通過MessageInspector實現。我們現在就來介紹這兩個組件的實現方式。---www.bianceng.cn

1、自定義ErrorHandler實現基於EHAB的異常處理和封裝

為了實現 利用EHAB自動處理服務操作拋出的異常,已經對處理後異常的封裝和傳遞,我定 義了如下一個自定義的ErrorHandler:ServiceErrorHandler。

1:  using System;
2: using System.ServiceModel;
3: using System.ServiceModel.Channels;
4: using  System.ServiceModel.Dispatcher;
5: using  Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
6:  namespace Artech.EnterLibIntegration.WcfExtensions
7:  {
8:     public class ServiceErrorHandler :  IErrorHandler
9:     {
10:          public string ExceptionPolicyName
11:         {  get; private set; }
12:         public  ServiceErrorHandler(string exceptionPolicyName)
13:          {
14:             if  (string.IsNullOrEmpty(exceptionPolicyName))
15:              {
16:                 throw new  ArgumentNullException("exceptionPolicyName");
17:              }
18:              this.ExceptionPolicyName = exceptionPolicyName;
19:          }
20:
21:         #region  IErrorHandler Members
22:         public bool  HandleError(Exception error)
23:         {
24:             return false;
25:          }
26:         public void ProvideFault (Exception error, MessageVersion version, ref Message fault)
27:         {
28:             if (typeof(FaultException).IsInstanceOfType(error))
29:              {
30:                  return;
31:             }
32:
33:             try
34:              {
35:                 if  (ExceptionPolicy.HandleException(error, this.ExceptionPolicyName))
36:                 {
37:                      fault = Message.CreateMessage(version,  BuildFault(error), ServiceExceptionDetail.FaultAction);
38:                  }
39:              }
40:             catch (Exception ex)
41:             {
42:                  fault = Message.CreateMessage(version, BuildFault(ex),  ServiceExceptionDetail.FaultAction);
43:              }
44:         }
45:
46:          private MessageFault BuildFault(Exception error)
47:         {
48:              ServiceExceptionDetail exceptionDetail = new  ServiceExceptionDetail(error);
49:              return MessageFault.CreateFault(FaultCode.CreateReceiverFaultCode (ServiceExceptionDetail.FaultSubCodeName,  ServiceExceptionDetail.FaultSubCodeNamespace),
50:                  new FaultReason(error.Message),  exceptionDetail);
51:         }
52:
53:         #endregion
54:     }
55:  }

在ServiceErrorHandler中定義的只讀屬性 ExceptionPolicyName表示服務端配置的異常處理策略的名稱,該屬性在構造函數 中指定。在ProvideFault方法中,先判斷拋出的異常是否是FaultException,如 果是則不作處理(在這種情況下,一般是服務提供者人為拋出的,並不希望再作 進一步的處理)。否則調用ExceptionPolicy的HandleException方法,傳入異常 處理策略名稱,對該異常進行處理。對於處理後的異常,通過BuildFault方法創 建ServiceExceptionDetail對象對異常信息進行封裝,並最終生成Fault消息。

2、自定義MessageInspector實現異常的重建

當封裝有異常信息的 Fault消息返回到客戶端後,需要將異常信息提取出來並通過反射重建並拋出異常 對象,我們通過自定義 MessageInspector來實現這樣的功能。為此,我們定義了 如下一個實現了IClientMessageInspector接口的類型: ExceptionHandlingMessageInspector。

1: using  System.ServiceModel;
2: using  System.ServiceModel.Channels;
3: using  System.ServiceModel.Dispatcher;
4: using System;
5: namespace Artech.EnterLibIntegration.WcfExtensions
6:  {
7:     public class  ExceptionHandlingMessageInspector : IClientMessageInspector
8:     {
9:         public void  AfterReceiveReply(ref Message reply, object correlationState)
10:         {
11:             if  (!reply.IsFault)
12:             {
13:                  return;
14:              }
15:
16:             if  (reply.Headers.Action == ServiceExceptionDetail.FaultAction)
17:             {
18:                  MessageFault fault = MessageFault.CreateFault(reply,  int.MaxValue);
19:                 if (fault.Code.SubCode.Name == ServiceExceptionDetail.FaultSubCodeName  &&
20:                      fault.Code.SubCode.Namespace ==  ServiceExceptionDetail.FaultSubCodeNamespace)
21:                  {
22:                      FaultException<ServiceExceptionDetail> exception =  (FaultException<ServiceExceptionDetail>) FaultException.CreateFault(fault, typeof(ServiceExceptionDetail));
23:                     throw GetException (exception.Detail);
24:                 }
25:             }
26:         }
27:
28:         private Exception  GetException(ServiceExceptionDetail exceptionDetail)
29:          {
30:             if (null ==  exceptionDetail.InnerException)
31:              {
32:                 return (Exception) Activator.CreateInstance(Type.GetType (exceptionDetail.AssemblyQualifiedName), exceptionDetail.Message);
33:             }
34:
35:              Exception innerException = GetException (exceptionDetail.InnerException);
36:              return (Exception)Activator.CreateInstance(Type.GetType (exceptionDetail.AssemblyQualifiedName), exceptionDetail.Message,  innerException);
37:         }
38:
39:         public object BeforeSendRequest(ref Message  request, IClientChannel channel)
40:         {
41:             return null;
42:          }
43:     }
44: }

在 AfterReceiveReply方法中,通過比較Fault消息的Action,以及SubCode的名稱和 命名空間確定接收到的消息正是服務端通過我們自定義的ServiceErrorHandler創 建。然後從中提取出封裝了異常信息的ServiceExceptionDetail對象,通過反射 的方式重新創建異常對象。

注:在創建異常對象的時候,默認調用的是參 數列表是 String(Message)和Exception(InnerException)類型的公共構造函 數,基本上絕大部分異常類型都具有這樣的構造函數。如果某個異常不具有這樣 的構造函數簽名,一般意味著並不希望異常對象從外部創建。比較典型的屬 SqlException,由於這樣的異常只能通過 System.Data.SqlClient數據存取提供 者(Data Access Provier)創建,所以並不具有我們希望的構造函數。對於這種 情況,在服務端必須利用替換機制將其替換成另一個可以創建的異常類型(比如 將 SqlException替換成自定義的DbException)。

3、通過行為應用自定 義ErrorHandler和MessageInspector

我們上面創建的自定義ErrorHandler (ServiceErrorHandler)和 MessageInspector (ExceptionHandlingMessageInspector),最終通過相應的WCF行為將它們分別 應用到 WCF服務端和客戶端運行時。我們可以采用4種行為(操作行為、契約行為 、終結點行為和服務行為)中的任何一種來實現,他們之間唯一的不同就是應用 的方式(自定義特性或者配置)和作用范圍不同。為此,我們創建了一個行為類 型:ExceptionHandlingBehaviorAttribute:

1: using  System;
2: using System.Collections.ObjectModel;
3: using System.ServiceModel;
4: using  System.ServiceModel.Channels;
5: using  System.ServiceModel.Description;
6: using  System.ServiceModel.Dispatcher;
7: namespace  Artech.EnterLibIntegration.WcfExtensions
8: {
9:      public class  ExceptionHandlingBehaviorAttribute:Attribute,IOperationBehavior,IContra ctBehavior,IEndpointBehavior,IServiceBehavior
10:      {
11:         public string ExceptionPolicyName
12:         { get; private set; }
13:
14:         public ExceptionHandlingBehaviorAttribute (string exceptionPolicyName)
15:         {
16:             if (string.IsNullOrEmpty (exceptionPolicyName))
17:             {
18:                 throw new  ArgumentNullException("exceptionPolicyName");
19:              }
20:              this.ExceptionPolicyName = exceptionPolicyName;
21:          }
22:
23:         #region  IOperationBehavior Members
24:         public void  AddBindingParameters(OperationDescription operationDescription,  BindingParameterCollection bindingParameters) {}
25:          public void ApplyClientBehavior(OperationDescription  operationDescription, ClientOperation clientOperation)
26:          {
27:              clientOperation.Parent.MessageInspectors.Add(new  ExceptionHandlingMessageInspector());
28:         }
29:         public void ApplyDispatchBehavior (OperationDescription operationDescription, DispatchOperation  dispatchOperation)
30:         {
31:               dispatchOperation.Parent.ChannelDispatcher.ErrorHandlers.Add(new  ServiceErrorHandler(this.ExceptionPolicyName));
32:               dispatchOperation.Parent.ChannelDispatcher.ErrorHandlers.Add(new  ServiceErrorHandler(this.ExceptionPolicyName));
33:          }
34:         public void Validate (OperationDescription operationDescription) {}
35:          #endregion
36:
37:         #region  IEndpointBehavior Members
38:         public void  AddBindingParameters(ServiceEndpoint endpoint,  BindingParameterCollection bindingParameters)
39:          {}
40:         public void  ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime  clientRuntime)
41:         {
42:              clientRuntime.MessageInspectors.Add(new  ExceptionHandlingMessageInspector());
43:         }
44:         public void ApplyDispatchBehavior (ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)  {}
45:         public void Validate(ServiceEndpoint  endpoint) {}
46:         #endregion
47:
48:         #region IServiceBehavior Members
49:         public void AddBindingParameters (ServiceDescription serviceDescription, ServiceHostBase  serviceHostBase, Collection<ServiceEndpoint> endpoints,  BindingParameterCollection bindingParameters)
50:          {}
51:         public void  ApplyDispatchBehavior(ServiceDescription serviceDescription,  ServiceHostBase serviceHostBase)
52:         {
53:             foreach (ChannelDispatcher  channelDispatcher in serviceHostBase.ChannelDispatchers)
54:              {
55:                  channelDispatcher.ErrorHandlers.Add(new ServiceErrorHandler (this.ExceptionPolicyName));
56:             }
57:         }
58:         public void  Validate(ServiceDescription serviceDescription,  System.ServiceModel.ServiceHostBase serviceHostBase){}
59:          #endregion
60:
61:          #region IContractBehavior Members
62:          public void AddBindingParameters(ContractDescription  contractDescription, ServiceEndpoint endpoint,  BindingParameterCollection bindingParameters){}
63:          public void ApplyClientBehavior(ContractDescription  contractDescription, ServiceEndpoint endpoint, ClientRuntime  clientRuntime)
64:         {
65:              clientRuntime.MessageInspectors.Add(new  ExceptionHandlingMessageInspector());
66:         }
67:
68:         public void  ApplyDispatchBehavior(ContractDescription contractDescription,  ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
69:         {
70:              dispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(new  ServiceErrorHandler(this.ExceptionPolicyName));
71:          }
72:         public void Validate (ContractDescription contractDescription, ServiceEndpoint endpoint)
73:         {}
74:          #endregion
75:     }
76: }

在 ApplyClientBehavior方法中,創建自定義的 ExceptionHandlingMessageInspector對象,並將其加入ClientRuntime的 MessageInspector列表中;在ApplyDispatchBehavior中,創建自定義的 ServiceErrorHandler對象,並將其加入到ChannelDispatcher的ErrorHandler列 表中。由於 ExceptionHandlingBehaviorAttribute既是操作行為,又是契約行為 和服務行為,同時又是一個自定義特性,所以我們可以直接通過特性的方式將其 應用到操作方法、契約接口或者類型和服務類型上面。在下面的代碼中,我們將 其應用到服務契約的Divide操作上面:

1: using  System.ServiceModel;
2: using  Artech.EnterLibIntegration.WcfExtensions;
3: namespace  Artech.WcfServices.Contracts
4: {
5:      [ServiceContract(Namespace = "http://www.artech.com/")]
6:      public interface ICalculator
7:     {
8:         [OperationContract]
9:          [ExceptionHandlingBehavior("myExceptionPolicy")]
10:          int Divide(int x, int y);
11:     }    
12: }

同時作為服務行為和終結點行為,我們又具 有另外一種服務應用的方式:配置。如果要實現通過配置方式應用該行為,我們 還需要定義對應的繼承自 System.ServiceModel.Configuration.BehaviorExtensionElement類型的配置元 素(Configuraiton Element)。為此,定義了如下一個類型: ExceptionHandlingBehaviorElement。

1: using System;
2: using System;
3: using System.Configuration;
4: using System.ServiceModel.Configuration;
5:  namespace Artech.EnterLibIntegration.WcfExtensions
6: {
7:     public class  ExceptionHandlingBehaviorElement:BehaviorExtensionElement
8:      {
9:         [ConfigurationProperty ("exceptionPolicy")]
10:         public string  ExceptionPolicy
11:         {
12:              get
13:             {return  this["exceptionPolicy"] as string;}
14:              set
15:             { this ["exceptionPolicy"] = value; }
16:         }
17:         public override Type BehaviorType
18:         {
19:             get {  return typeof(ExceptionHandlingBehaviorAttribute); }
20:          }
21:         protected override  object CreateBehavior()
22:         {
23:              return new  ExceptionHandlingBehaviorAttribute(this.ExceptionPolicy);
24:          }
25:     }
26: }

這樣,我們就可以通過配置的方式將其應用到某個服務(作為服 務行為)或者終結點(作為終結點行為)上了。在下面的配置中,我將此行為應 用到CalculatorService服務上面。

1: <?xml version="1.0"  encoding="utf-8"?>
2: <configuration>
3:    <system.serviceModel>
4:     <behaviors>
5:       <serviceBehaviors>
6:          <behavior name="exceptionHandling">
7:            <exceptionHandling  exceptionPolicy="myExceptionPolicy" />
8:          </behavior>
9:        </serviceBehaviors>
10:     </behaviors>
11:     <extensions>
12:        <behaviorExtensions>
13:         <add  name="exceptionHandling"  type="Artech.EnterLibIntegration.WcfExtensions.ExceptionHandlingBehavio rElement, Extensions, Version=1.0.0.0, Culture=neutral,  PublicKeyToken=null" />
14:        </behaviorExtensions>
15:      </extensions>
16:     <services>
17:        <service behaviorConfiguration="exceptionHandling"  name="Artech.WcfServices.Services.CalculatorService">
18:          <endpoint  address="http://127.0.0.1:3721/calculatorservice"  binding="ws2007HttpBinding"  contract="Artech.WcfServices.Contracts.ICalculator" />
19:        </service>
20:      </services>
21:   </system.serviceModel>
22: </configuration>

三、實例演示

接下來 我們我們將上面我們定義的行為應用到真正的實例之中,看看它們是否會按照我 們之前希望的方式進行異常的處理。簡單起見,我們還是用我們熟悉的計算服務 的例子。現在,我們將ExceptionHandlingBehaviorAttribute作為操作行為應用 到服務契約接口 ICalculator的Divide操作方法上,並指明異常處理策略名稱 (myExceptionPolicy)。

1: using System.ServiceModel;
2: using Artech.EnterLibIntegration.WcfExtensions;
3: namespace Artech.WcfServices.Contracts
4: {
5:     [ServiceContract(Namespace =  "http://www.artech.com/")]
6:     public interface  ICalculator
7:     {
8:          [OperationContract]
9:          [ExceptionHandlingBehavior("myExceptionPolicy")]
10:          [FaultContract(typeof(ServiceExceptionDetail), Action =  "http://www.artech.com/fault")]
11:         int  Divide(int x, int y);
12:     }   
13: }

細心的讀者可能注意到了:在Divide操作上面還同時應用了 FaultContractAttribute特性,並將 ServiceExceptionDetail類型作為錯誤明細 類型。這樣做的目的在於:用於封裝異常信息的 ServiceExceptionDetail類型必 須作為錯誤契約,其對象才能被FaultFormatter序列化和反序列化。所以,將 ServiceExceptionDetail作為錯誤契約時必須的。下面是服務類型的代碼:

1: using System.ServiceModel;
2: using  Artech.WcfServices.Contracts;
3: namespace  Artech.WcfServices.Services
4: {
5:      [ServiceBehavior(Namespace="http://www.artech.com/")]
6:      public class CalculatorService : ICalculator
7:      {
8:         public int Divide(int x,  int y)
9:         {
10:              return x / y;
11:         }
12:      }
13: } 

接下來,我們利用微軟企業庫提供 的配置工具(Configuration Console)定義如下的異常處理策略,並命名為在 ExceptionHandlingBehaviorAttribute指定的名稱:myExceptionPolicy。此策略 專門針對在Divide操作中會跑出的DivideByZeroException異常類型。

1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3:    <configSections>
4:     <section  name="exceptionHandling"  type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configura tion.ExceptionHandlingSettings,  Microsoft.Practices.EnterpriseLibrary.ExceptionHandling,  Version=4.1.0.0, Culture=neutral,  PublicKeyToken=31bf3856ad364e35" />
5:    </configSections>
6:   <exceptionHandling>
7:     <exceptionPolicies>
8:        <add name="myExceptionPolicy">
9:          <exceptionTypes>
10:           <add  type="System.DivideByZeroException, mscorlib, Version=2.0.0.0,  Culture=neutral, PublicKeyToken=b77a5c561934e089"
11:              postHandlingAction="ThrowNewException"  name="DivideByZeroException">
12:              <exceptionHandlers>
13:                <add exceptionMessage="計算錯誤" 
14:  wrapExceptionType="Artech.WcfServices.Contracts.CalculationException,Ar tech.WcfServices.Contracts"                 type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandl er, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling,  Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
15:                 name="Wrap  Handler" />
16:              </exceptionHandlers>
17:            </add>
18:         </exceptionTypes>
19:       </add>
20:      </exceptionPolicies>
21:    </exceptionHandling>
22:  </configuration>

該異常策略定義非常簡單,僅僅是將 DivideByZeroException異常封裝成我們自定義的CalculationException異常(封 裝後,原來的DivideByZeroException異常將會作為CalculationException異常的 InnerException),並指定異常消息("計算錯誤")。CalculationException僅 僅是一個普通的自定義異常:

1: using System;
2:  namespace Artech.WcfServices.Contracts
3: {
4:     [global::System.Serializable]
5:     public  class CalculationException : Exception
6:     {
7:         public CalculationException() { }
8:         public CalculationException(string message)  : base(message) { }
9:         public  CalculationException(string message, Exception inner) : base (message, inner) { }
10:         protected  CalculationException(
11:            System.Runtime.Serialization.SerializationInfo info,
12:            System.Runtime.Serialization.StreamingContext  context)
13:             : base(info, context)  { }
14: }
15: }

最後,下面是客戶端 的代碼,運行我們的應用程序,客戶端將會得到如下的輸出。

1:  using System;
2: using System.ServiceModel;
3:  using Artech.WcfServices.Contracts;
4: using  Artech.EnterLibIntegration.WcfExtensions;
5: namespace  Artech.WcfServices.Clients
6: {
7:      class Program
8:     {
9:          static void Main(string[] args)
10:         {
11:             using  (ExceptionHandlingChannelFactory<ICalculator> channelFactory =  new ExceptionHandlingChannelFactory<ICalculator>(
12:                 "calculatorservice"))
13:              {
14:                  ICalculator calculator = channelFactory.CreateChannel();
15:                 using (calculator as  IDisposable)
16:                 {
17:                     try
18:                      {
19:                          int result = calculator.Divide(1, 0);
20:                     }
21:                      catch (CalculationException ex)
22:                     {
23:                          Console.WriteLine (ex.Message);
24:                          Console.WriteLine("InnerException");
25:                          Console.WriteLine("\tType:{0}",  ex.InnerException.GetType());
26:                          Console.WriteLine("\tMessage:{0}",  ex.InnerException.Message);
27:                      }
28:                 }
29:             }
30:
31:              Console.Read();
32:         }
33:     }

輸出結果:

計算錯誤
InnerException
Type:System.DivideByZeroException
Message:試圖除以 零.

本文配套源碼

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