首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WCF 技術深度剖析(十):客戶端異常處理與服務代理正確關閉模式

     編輯:關於.NET

前言:關閉代理不等於萬事大吉

上一篇強調及時關閉服務代理(Close())對釋放會話資源的重要性。但現實是:當服務端拋出異常時,代理進入 Faulted 狀態,直接呼叫 Close() 會再次拋出 CommunicationObjectFaultedException。本文深入剖析原因,並給出生產級的異常處理最佳實踐。

一、問題重現:異常導致 Close 失敗

使用 WSHttpBinding(預設啟用會話),服務端拋出 DivideByZeroException。客戶端 finally 區塊呼叫 Close() 時拋出異常:

CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

二、根因分析:會話通道 vs. 資料報通道

  • 會話通道:訊息有關聯性,通道狀態機包含 Opened→Faulted。異常導致通道進入 Faulted,後續任何操作(含 Close())皆失敗。
  • 資料報通道:每次交換獨立,無狀態,異常不影響後續通訊。

WSHttpBinding 預設 SecurityMode.Message → 啟用會話。若設為 SecurityMode.None 則為資料報通道,Close() 不會失敗(但也失去會話優勢)。

三、正確模式:Try-Catch-Abort/Close

針對會話通道,必須捕獲通訊異常並呼叫 Abort() 強制釋放資源:

using (var factory = new ChannelFactory<ICalculator>("calculateservice"))
{
    ICalculator proxy = factory.CreateChannel();
    try
    {
        int result = proxy.Divide(1, 0); // 觸發異常
        (proxy as ICommunicationObject).Close(); // 正常路徑
    }
    catch (CommunicationException ex)
    {
        (proxy as ICommunicationObject).Abort(); // 強制關閉
        // 記錄日誌、重試邏輯...
        throw;
    }
    catch (TimeoutException ex)
    {
        (proxy as ICommunicationObject).Abort();
        throw;
    }
    catch (Exception ex)
    {
        // 其他業務異常,通道未必 Faulted,嘗試 Close
        (proxy as ICommunicationObject).Close();
    }
}

四、封裝重用:消除樣板代碼

利用委派封裝通用異常處理邏輯:

static TReturn Invoke<TContract, TReturn>(TContract proxy, Func<TContract, TReturn> func)
{
    TReturn ret = default;
    try
    {
        ret = func(proxy);
        (proxy as ICommunicationObject).Close();
    }
    catch (CommunicationException)
    {
        (proxy as ICommunicationObject).Abort(); throw;
    }
    catch (TimeoutException)
    {
        (proxy as ICommunicationObject).Abort(); throw;
    }
    catch (Exception)
    {
        (proxy as ICommunicationObject).Close(); // 非通訊異常嘗試正常關閉
    }
    return ret;
}

// 使用極簡潔
int result = Invoke(proxy, p => p.Divide(2, 1));

同理提供 Action 版本供無返回值操作。

總結

  • 會話通道異常 → Faulted → 必須 Abort()。
  • 捕獲 CommunicationException、TimeoutException 並 Abort 是鐵律。
  • 封裝通用 Invoke 輔助方法,統一錯誤處理,提升代碼品質。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved