1. 程式人生 > >不要在using語句中呼叫WCF服務

不要在using語句中呼叫WCF服務

如果你呼叫WCF服務時,像下面的程式碼這樣在using語句中進行呼叫,需要注意一個問題。

using (CnblogsWcfClient client = new CnblogsWcfClient())
{
    client.Say("Hello, cnblogs.com!");
}

上面這段程式碼看上去沒問題,CnblogsWcfClient是一個自動生成的WCF客戶端代理,繼承自System.ServiceModel.ClientBase。using語句結束時,會呼叫ClientBase實現的System.IDisposable.Dispose介面,實際就是呼叫ClientBase的Close()方法。

用.NET Refector開啟C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.ServiceModel.dll,可以看到這樣的程式碼,見下圖:

在這裡插入圖片描述

不僅看上去沒問題,似乎就是沒問題。但是…問題就出在ClientBase.Close()上,Close()要關閉的是一個網路連線,如果這時網路連接出現問題,不能正常關閉會引發異常(ClientBase的Close方法就是這樣設計的,引發異常,而不是強制關閉),問題就來了。本來我們使用using的目的就是不管出現什麼狀況,即使天塌下來,也給我關閉掉;結果,關是關了,卻沒有閉,天還是塌下來了。

也許我們可以用“不可抗拒力”迴避這個問題,但程式設計師的天性是解決問題。程式碼中任何一個小問題都不能忽視,因為我們很難預料這個小問題會不會帶來大問題。

如何解決:
增加一個擴充套件方法,那麼就可以比較優雅的呼叫,而且不會出錯了。
客戶端呼叫程式碼如下:

string where = GetSearchSql();
new EnterpriseServiceClient().Using(enterpriseClient =>
{
    this.winGridViewPager1.AllToExport = enterpriseClient.FindToDataTable(where);
});
/// <summary>
/// WCF服務包裝類,避免使用Using等方式導致服務出錯的問題
/// </summary>
public static class WcfExtensions
{
    public static void Using<T>(this T client, Action<T> work)
        where T : ICommunicationObject
    {
        try
        {
            work(client);
            client.Close();
        }
        catch (CommunicationException e)
        {
            client.Abort();
        }
        catch (TimeoutException e)
        {
            client.Abort();
        }
        catch (Exception e)
        {
            client.Abort();
            throw;
        }
    }
}