How to abort the WCF/Web Service in WP7(Silverlight)
在撰寫WP7(Silverlight)時,如果用戶對於當前作業呼叫了WCF或Web Service後,在結果尚未回覆就立即離開本作業,
針對已經發出Request的任務,其實是不會被停止下來的,這樣在下一次進入同一個作業時,將會先收到上一次所發出
Request產生的結果,這是什麼原因,主要是因為在Silverlight裡,提供非同步()的呼叫方式,造成會有如同上述的問題。
既然有上述的情形,在撰寫WP7時,最好的辦法就是在用戶按下Back鍵時,就該把服務給終止掉,以免重覆出現的問題。
但按照我直接寫入「gSoapClient.Abort();」的程式碼時,系統直接出現了如下的錯誤訊息:
「The HTTP request to 'http://localhost/WebServices/Service1.asmx' was aborted. This may be due to the local channel
being closed while the request was still in progress. If this behavior is not desired, then update your code so that it does not
close the channel while request operations are still in progress.」
以下將介紹相關WCF進行溝通時的主要元件:
撰寫WCF程式時,如果是初學者通常不會去注意到WCF最底層通訊的結構,大致上會以先了解如何定義服務與傳輸資料的契約。
然而,在WCF裡ICommunicationObject介面是最底層的實作介面之一,負責了所有相關溝通的一切,包括:Channel定義、管理、
factories、listeners以及dispatchers和service hosts。提供了相當多的方法給予實作它的類別獨立增加需要任務,包括:abort、
open、close等,讓它們可以針對目前的WCF進行狀態的管理與服務的監控。
然而,ICommunicationObject的角色主要是被實作的,但透過它可以控制目前WCF服務的State與Machine的溝通狀況。
其中有三個主要的狀態需要被注意:
(a) Created:
一開始整個Communication Object的初始狀態,在該狀態下需完成所有Channel或Contact相關的Configuratoin與Resources設定。
注意:「一旦Communication Object離開了Created狀態,就無法在去調整相關的設定或Channel stack。」因此,也被稱為是一種
immutable的物件。
(b) Opened:
在Communication Object在Created階段後呼叫同步的(Open())或非同步的(BeginOpen())方法,則進入Opened的狀態。
只有Object的State在Opend的狀態下才可以進行傳送或接收訊息。在Open時要記得針對二個Exception進行處理:
CommunicationException與TimeoutException。這二個分別代表一為啟動時發生會誤,二為啟動時間超出預期的時間。
(b) Closed:
在Communication Object直接呼叫同步的(Close())或非同步的(BeingClose())方法,則要求Object進入Closed的狀態。
呼叫Close方法將會把任何未完成的工作,要求它在完成之前就返回。但要注意的是,BeningClose()在非同步要求close時,
很容易出現Exception,因此,為確保程式能正常且可預期的Exception,最好加上CommunicationObjectFaultedException。
這三個狀態的階段將會影響在程式撰寫時,啟動服務Request與Response的重要處理階段,在Close()的呼叫,在WP7裡我建議最好
在離開當前頁面或作業時,最好加上Close()或Abort(),可以讓重覆進入Page時,不會因為服務非同步造成重覆的問題。
提到ICommunication Object一定要接著往下提ChannelFactory類別,該類別實作了三個重要的元件:
CommunicationObject, IChannelFactory, ICommunicationObject。該類別用於建立和管理由用戶端用於傳送訊息至服務端點的通道。
這也代表說程式裡所呼叫的WCF/Web Service會透過建立該Channel,讓我們程式裡可以直接叫用定義於WCF契約中的Operation,
然而ChannelFactory類別負責包括了上述提及的Communication Object之外,更可以控制其Channel一些相關的設定,包括:
(a) 認證:包括SSL、Windows驗證或User驗證;
(b) Default Close Timeout與Default Open Timeout:這二個在上述提及的State變換,都是以此為識別依據;
(c) ServiceEndpoint:取得由Factory產生的Channel所要連接的服務端點。
(d) States:由於該類別實作了ICommunicationObject介面,因此,重要的三種State都在該類別中可以取得;
由於它實作了三個重要的元件,所以該類別其實就是我們操作WCF的真正實體,透過ChannelFactory類別,更可以配合
定義好的Contact來產生對應的Channel,實作的範例,可以參考<ChannelFactory 類別 - 範例>。
然而,這二個類別其實沒有這麼簡單,關於WCF的實作更包括相關的注意事項,在本篇主要針對我在開發過程遇到的問題,
針對重要的元件做一些概論的說明,接下來進入本篇要解的問題:
〉Abort the WCF, and try catch … the Exception:
針對呼叫Communication Object中的Abort()方法後,發出Exception的通知,其錯誤訊息是在說明,目前的Request Channel是被強迫關閉的,
因為正常的Http協定應該有Request對應Response,但今天我們強迫發出取消的動作,因此也是強迫Object由Opened狀態忽然轉成Closed。
那該怎麼辦?以目前我的作法是直接透過try…catch的方式,將預期的CommunicationObjectAbortedException加以阻擋下來,並且強迫釋放
Communication Object元件。
如下程式碼:
1: public PouModule.DynAppWS.SearchFollowingResponse EndSearchFollowing(System.IAsyncResult result)
2: {
3: PouModule.DynAppWS.SearchFollowingResponse _result =
4: new SearchFollowingResponse(new SearchFollowingResponseBody(""));
5: try
6: {
7: object[] _args = new object[0];
8: //呼叫wcf服務
9: _result = ((PouModule.DynAppWS.SearchFollowingResponse)
10: (base.EndInvoke("SearchFollowing", _args, result)));
11: }
12: catch (CommunicationObjectAbortedException ex)
13: {
14: //監聽呼叫abort所送出的Exception
15: string tX = ex.Message;
16: }
17: return _result;
18: }
〉針對WCF的Timeout問題,如何改善?:
針對加入專案中的WCF或Web Service參考,如果遇到因為某些因素造成Request出現Timeout的Exception時,要怎麼解決呢?
預設加上Web參考的Service會轉成一個對應呼叫的類別,其預設的Timeout時間為:1分鐘。因此,按照下方的程式碼:
1: //設定Timeout時間為:2分鐘
2: gSoapClient.InnerChannel.OperationTimeout = TimeSpan.FromMinutes(2);
其中有二個重要的屬性:
‧InnerChannel:用於取得實作IClientChannel介面的物件,其介面定義了用戶端應用程式所用之傳出要求/回覆通道的行為。
簡單來說,該InnerChannel代表的是當我們呼叫WCF進行服務運作時會建立一個服務Channel,其Channel中定義了相關的服
務運作規範與契約。
‧OperationTimeout:取得或設定作業必須完成的期間,否則會擲回例外狀況。資料格式為:TimeSpan。
透過上述二個屬性的定義,即可以讓呼叫時針對Request的Timeout多增加指定的時間範圍,才不會預設只有1分鐘可以使用。
======
以上是簡單分享一下自己在撰寫WCF練習時所遇到的問題,一開始覺得非常奇怪,因為自己撰寫的WCF也有實作ICommunicationObject,
怎麼無法直接Abort()目前正在運作的Reuqest呢,後來才發現在WCF裡,當下達Request來進行Operations的運作時,其實它是開啟了一個
獨立的Channel來服務你的Request,因此,直接使用這個Object來執行Abort()方法,其實它會不明白究竟真正要Abort是那一個Channel。
這是要特別注意的。
References:
‧System.ServiceModel 命名空間 (必讀)
‧close vs abort ChannelFactory WCF
‧Building a Reusable Service Client (重要)
‧WCF托管特性ICommunicationObject接口实现 (必要)
‧IDuplexSession.CloseOutputSession
‧Using IDisposable on WCF Proxies (or any ICommunicationObject implementation)
‧Meet the Channel Model: ICommunicationObject
‧Introducing ICommunicationObject
‧Asynchronous operations, pinning (重要)
‧MSDN 教學短片 – WCF & Windows Communication Foundation
‧BasicHttpBinding Class & Timeout error in WCF