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

Enterprise Library深入解析與靈活應用(8):WCF與Exception Handling AppBl

編輯:關於.NET

Enterprise Library深入解析與靈活應用(8):WCF與Exception Handling AppBlock集成[下]

在上篇中,我詳細介紹了如何通過自定義ClientMessageInspector和 ErrorHandler,實現WCF與微軟企業庫中的Exception Handling Application Block(EHAB)之間的集成。這個方案的基本思路就是:當異常從服務端拋出,利 用EHAB針對某個配置好的異常處理策略進行處理;然後將處理有的異常通過 ServiceExceptionDetail對象進行封裝,最後序列化置於Fault消息,最終被返回 給客戶端;客戶端接收到該Fault消息後,提取並創建ServiceExceptionDetail對 象,並通過反射重建異常;最後將異常拋出,使客戶端可以根據客戶端配置的異 常處理策略對該異常進行進一步的處理。

為了實現WCF對 ServiceExceptionDetail對象的序列化和反序列化,我們必須通過 FaultContractAttribute特性將類型定義成錯誤契約,相應的形式如下面的代碼 所示。在一般的情況下,如果你定義的服務是為他人所用,比如第三方服務消費 者,該錯誤契約的定義是必須的,因為相應的錯誤明細類型需要通過元數據的形 式發布出來,指導客戶端如何對接收到的消息進行反序列化。但是,如果服務僅 供你自己的應用所用,那麼你可以在運行時動態地添加相應的錯誤描述,從而避 免在服務契約的每一個服務操作方法上應用這麼一個FaultContractAttribute。

1: [ServiceContract(Namespace =  "http://www.artech.com/")]
2: public interface  ICalculator
3: {
4:      [OperationContract]
5:     [ExceptionHandlingBehavior ("myExceptionPolicy")]
6:     [FaultContract(typeof (ServiceExceptionDetail), Action = "http://www.artech.com/fault")]
7:     int Divide(int x, int y);
8: }

我們應用在操作方法上面的FaultContractAttribute特性,最終 會轉換成操作描述(OperationDescription)的錯誤描述(FaultDescription) ,如果我們在運行時能夠為所有的操作描述添加相應的錯誤描述,就能避免在每 個服務操作上面應用相同的FaultContractAttribute特性。不過,為了服務的重 用,我不介意這樣偷懶,所以這種方案僅僅作為研究、學習之用。

一、通 過自定義ServiceHost的方式動態添加錯誤描述(服務端)

首先需要在服 務端為每一個服務操作添加基於ServiceExceptionDetail的錯誤描述,這可以通 過自定ServiceHost來實現。由於服務描述需要在ServiceHost開啟之前生成方才 有效(具體的原因,相對比較復雜,大家可以在《WCF技術剖析(卷1)》第7章關於 服務寄宿的部分找到答案),所以我們將相關的邏輯定義在OnOpening方法之中。 在下面的代碼中,我定義了這樣一個簡單的ServiceHost: ExceptionHandlingServiceHost。

1: using System;
2: using System.ServiceModel;
3: using  System.ServiceModel.Activation;
4: using  System.ServiceModel.Description;
5:
6: namespace  Artech.EnterLibIntegration.WcfExtensions
7: {
8:     public class ExceptionHandlingServiceHost :  ServiceHost
9:     {
10:          public ExceptionHandlingServiceHost(Type t, params Uri[]  baseAddresses)
11:             : base(t,  baseAddresses)
12:         { }
13:
14:         protected override void OnOpening()
15:         {
16:              base.OnOpening();
17:             foreach  (ServiceEndpoint endpoint in this.Description.Endpoints)
18:             {
19:                  foreach (OperationDescription operation in  endpoint.Contract.Operations)
20:                  { 
21:                      FaultDescription faultDescription = new FaultDescription (ServiceExceptionDetail.FaultAction);
22:                      faultDescription.DetailType = typeof (ServiceExceptionDetail);
23:                      operation.Faults.Add(faultDescription);
24:                  }
25:             }
26:         }
27:     }
28: }

邏輯相對比較簡單:遍歷所有終結點(ServiceEndpoint),為每 一個終結點的契約(ContractDescription)的每一個操作 (OperationDescription)添加錯誤明細類型為ServiceExceptionDetail的錯誤 描述(FaultDescription),並指定預定義的Action。

對於自定義的 ServiceHost,可以直接用於不需要.svc文件進行訪問的寄宿場景,也就是說對於 除了IIS和WAS的服務寄宿,可以直接采用自定義的ServiceHost。服務需要在基於 IIS和WAS的寄宿方式中采用自定義的ServiceHost,還需要為之創建相應的 ServiceHostFactory(關於ServiceHostFactory作用和用法,同樣可以參閱《WCF 技術剖析(卷1)》第7章)。下面,我們為ExceptionHandlingServiceHost定義了 一個簡單的ServiceHostFactory:ExceptionHandlingServiceHostFactory。

1: using System;
2: using  System.ServiceModel;
3: using  System.ServiceModel.Activation;
4: using  System.ServiceModel.Description;
5:
6: namespace  Artech.EnterLibIntegration.WcfExtensions
7: {
8:     public class ExceptionHandlingServiceHostFactory :  ServiceHostFactory
9:     {
10:          protected override ServiceHost CreateServiceHost(Type  serviceType, Uri[] baseAddresses)
11:         {
12:             return new  ExceptionHandlingServiceHost(serviceType, baseAddresses);
13:          }
14:     }
15: }

二、通過自定義ChannelFactory<TChanel>的方式動態添加 錯誤描述(客戶端)

服務端需要為每一個操作添加基於 ServiceExceptionDetail的錯誤描述,以便實現對該對象的序列化;同理,客戶 端同樣需要這樣一個錯誤描述,以實現對該對象的反序列化。我們可以將這樣的 功能通過一個自定義ChannelFactory<TChannel>來實現。下面定義的 ExceptionHandlingChannelFactory就是這樣一個自定的 ChannelFactory<TChannel>。對錯誤描述的添加實現在重寫的 CreateDescription方法中:

1: using  System.ServiceModel;
2: using  System.ServiceModel.Description;
3:
4: namespace  Artech.EnterLibIntegration.WcfExtensions
5: {
6:     public class  ExceptionHandlingChannelFactory<TChannel>:ChannelFactory<TChan nel>
7:     {
8:         public  ExceptionHandlingChannelFactory(string endpointConfigurationName)
9:             : base(endpointConfigurationName)
10:         { }
11:
12:          protected override ServiceEndpoint CreateDescription()
13:         {  
14:              ServiceEndpoint serviceEndpoint = base.CreateDescription();
15:             foreach (OperationDescription  operation in serviceEndpoint.Contract.Operations)
16:              {
17:                  FaultDescription faultDescription = new FaultDescription (ServiceExceptionDetail.FaultAction);
18:                  faultDescription.DetailType = typeof (ServiceExceptionDetail);
19:                  operation.Faults.Add(faultDescription);
20:              }
21:
22:             return  serviceEndpoint;
23:         }
24:      }
25: }

三、實例演示

那麼,對其我們給出的 例子,我們就要使用我們上面創建的這兩個組件了。首先,有了這兩個組件的幫 助,在服務契約中,我們再也不需要在繁瑣地為每一個服務操作定義相同的 FaultContractAttribute特性了。於是我們先將其拿掉。

1:  [ServiceContract(Namespace = "http://www.artech.com/")]
2:  public interface ICalculator
3: {
4:      [OperationContract]
5:      [ExceptionHandlingBehavior("myExceptionPolicy")]
6:      int Divide(int x, int y);
7: }   

然 後,再進行服務寄宿的時候,直接利用我們定義的 ExceptionHandlingServiceHost就可以了。

1: using  System;
2: using  Artech.EnterLibIntegration.WcfExtensions;
3: using  Artech.WcfServices.Services;
4: namespace  Artech.WcfServices.Hosting
5: {
6:      public class Program
7:     {
8:          static void Main(string[] args)
9:          {
10:             using  (ExceptionHandlingServiceHost host = new  ExceptionHandlingServiceHost(typeof(CalculatorService)))
11:              {
12:
13:                  host.Open();
14:                  Console.Read();
15:             }
16:          }
17:     }
18: }

而客戶端,我們可以借助於我們定義的 ExceptionHandlingChannelFactory<TChannel>實現對服務代理的創建。

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

這樣我們同樣可以得到與 上篇一樣的執行結果:

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

本文配套源碼

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