WCF入門(七)——異常處理1


首先以一個簡單的例子演示一下遠程調用發生異常的結果:

服務器端代碼如下:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void ErrorTest();
    }

    public class Service1 : IService1
    {
        public void ErrorTest() { throw new InvalidOperationException("
異常測試
");
    }

該服務非常簡單,直接拋一個異常。當運行客戶端代碼觸發這個異常時,客戶端會接收到如下異常:

  

從中我們可以看到:雖然服務器端拋出的是InvalidOperationException異常,但是客戶端接收到的卻是FaultException異常。從WCF測試客戶端的傳遞的消息中也可以看出這一點。

  

WCF異常處理機制

  1. 通訊異常,這通常是因為鏈路的原因,比如服務沒有啟動,網絡阻塞等。通常是CommunicationException或者其派生類
  2. 超時異常,這類異常是因為操作時間超時,超時時會拋出 TimeoutException
  3. 服務異常,由服務端根據具體的業務邏輯觸發,當業務邏輯拋出異常時,會將其封裝成FaultException拋給客戶端。

通信異常和超時異常是由WCF框架所產生的,由於網絡原因,它們出現是正常的,因此也被稱為預期異常,是正常調用的時候需要處理的異常。而FaultException異常則是業務邏輯中異常情況時出現的異常,是由自己實現的代碼中產生的,是非預期的異常。具體處理方式根據業務邏輯而定。

但值得注意的是:服務被設置為PerSession模式或者Single模式,服務異常還會導致服務對象被釋放而終止服務這個是在網上看到的說明,但我自己試的時候沒有掛,從WCF服務對象模型上來分析也不應該會掛,也沒有看到MSDN上哪兒有寫,待后續確認后再更新

至於為什么要將CLR異常封裝成FaultException后拋出,我的理解是:對於異常信息,是序列化后才發布給客戶端的,也就是說,客戶端也得需要理解這個異常才能反序列化異常信息。因此,客戶端必須知道異常的格式,由於WCF的客戶端並非只有.net語言才能實現,因此需要發布異常格式,由於CLR的異常比較多,加上還有自定義異常,無法公布所有異常格式,因此,將其統一成FaultException發布,這樣客戶端才能順利解析。

了解了這個后,我們就知道InvalidOperationException異常為什么會變成了FaultException異常,但是存在的一個問題是:雖然客戶端能感知到調用服務發生了異常,但仍然不知道異常信息,有時無法進行進一步處理。

Debug期間異常信息傳遞

在前面的異常信息中就有說明:有關該錯誤的詳細信息,請打開服務器上的 IncludeExceptionDetailInFaults (從 ServiceBehaviorAttribute 或從 <serviceDebug> 配置行為)以便將異常信息發送回客戶端。它告訴了我們有兩個方式可以傳遞這個異常信息:

  1. 配置serviceBehaviors,將includeExceptionDetailInFaults設置為true

  

  1. 在服務上的ServiceBehaviorAttribute里面設置IncludeExceptionDetailInFaults

  

這兩種方式是等效的,不過建議在配置文件里面使用,統一設置比較方便,也方便統一關閉。

經過這個設置后,再運行客戶端代碼,這次就可以看到異常信息了:

  

連異常的調用棧也能看到,方便我們進行異常定位。

<StackTrace> WcfService.Service1.ErrorTest() 位置 Service1.cs:行號24
SyncInvokeErrorTest(Object , Object[] , Object[] )

不過,這個在另一方面也暴露了服務器的信息,對於這種網絡程序來說是不安全的,因此只建議在Debug的時候使用,從serviceDebug的名字中也可以看出這一點。

構造FaultException

為了安全,前面所說的異常信息獲取方式,只能在Debug期間使用,那么,對於Release版本,則需要我們自己手動把運行異常封裝成FaultException返回。

    public class Service1 : IService1
    {
        public void ErrorTest()
        {
            try
            {
                throw new InvalidOperationException("
異常測試
");
            }
            catch (Exception e)
            {
                throw new FaultException(e.Message);
            }
        }
    }

這次我們就能獲取到異常信息了。

  

對於比較復雜的異常信息,可以自己構造一個FaultException<T>,如下是一個簡單的示例:

    [ServiceContract]
    public interface Iservice
    {
        [OperationContract]
        [
FaultContract(typeof(DataAccessFault))]
        void Operation();
    }

    public class Service : Iservice
    {
        public void Operation()
        {
            try
            {
                // Access database …
            }
            catch (DbException e)
            {
                DataAccessFault fault = new DataAccessFault();
                fault.AdditionalDetails = e.Message;
                throw new FaultException<DataAccessFault>(fault);
            }
        }
    }

自己手動構造FaultException的方式比較麻煩,需要在每一個提供的接口中都捕獲異常,並重新封裝拋出,一來不好看,處理起來也比較麻煩。由於篇幅有限,下篇文章中介紹一個更為簡單的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM