2011-03-28 97 views
15

我們從Web應用程序中獲取了一個WCF服務。我們使用的客戶端是使用Visual Studio的「Add Service Reference」選項生成的。由於它是一個網絡應用程序,並且由於應用程序的性質很可能會導致相對較短的會話,因此我們選擇在用戶登錄並在會話的整個生命週期內保留該客戶端時創建客戶端實例,然後在會話結束時處理它。處理持久WCF客戶端進入故障狀態

這使我想到了我的問題 - 我們試圖確定處理客戶端通道進入故障狀態的最佳方式。周圍的一些搜索後,我們來到了這一點:

if(client.State = CommuncationState.Faulted) 
{ 
    client = new Client(); 
} 

try 
{ 
    client.SomeMethod(); 
} 
catch //specific exceptions left out for brevity 
{ 
    //logging or whatever we decide to do 
    throw; 
} 

然而,這並不工作,由於這樣的事實,至少在我們的情況下,即使服務已關閉,客戶端會顯示Open狀態,直到您實際嘗試使用它進行呼叫,然後進入Faulted狀態。

所以這讓我們去做別的事情。我們想出的另一個選項是:

try 
{ 
    client.SomeMethod(); 
} 
catch 
{ 
    if(client.State == CommunicationState.Faulted) 
    { 
     //we know we're faulted, try it again 
     client = new Client(); 
     try 
     { 
      client.SomeMethod(); 
     } 
     catch 
     { 
      throw; 
     } 
    } 
    //handle other exceptions 
} 

但是那種氣味。顯然,我們可以通過使用新客戶端並在每次調用時處理它來避免這種情況。這似乎是不必要的,但如果這是正確的方式,那麼我想這就是我們會選擇的。那麼,優雅地處理確定客戶是否處於故障狀態,然後對其進行處理的最佳方式是什麼?我們是否應該爲每次通話都獲得新客戶?

要記住的另一件事 - 客戶端的實例化以及所有這些檢查和處理髮生在客戶端的包裝類中。如果我們按照我們的意圖這樣做,它對應用程序本身就是透明的 - 調用它們並處理它們的異常不需要特殊的代碼。

+0

什麼導致客戶端進入故障狀態?我一直可以讓WCF服務正常返回錯誤,客戶可以繼續關注它的業務。服務器沒有響應或什麼? – Tridus 2011-03-28 23:16:45

+0

在這種情況下,我們使用ASP.NET成員資格,並且通過超出「userIsOnlineTimeWindow」屬性來運行它。很明顯,在這種情況下,將用戶重定向到登錄頁面是有意義的,但我們正在努力確保我們已準備好應對可能陷入故障狀態的其他任何情況。 – Zannjaminderson 2011-03-29 13:30:04

回答

18

要回答你的問題,你可以處理的ChannelFactory屬性的Faulted event這樣的:

client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted); 

這應該讓你執行任何記錄/你需要做清理工作。

作爲一般性建議,您不應該在會話期間將通道保持打開狀態,因此請確保在完成通道後正確關閉通道(異常中止)。如果可能的話,考慮不使用Visual Studio添加服務引用,或者至少清理它生成的代碼/配置。我建議如果您想使用代理實現,請通過從ClientBase派生或使用ChannelFactory實現來創建自己的代理實現。既然你提到了一個包裝類,我建議你使用ChannelFactory併爲你的清理需求處理Faulted event

+0

感謝您的回答。我已經看到很多人在爲每個電話打開和關閉一個客戶頻道,我想知道這樣做的理由是什麼? – Zannjaminderson 2011-03-29 13:31:33

+2

原因是您的服務只能有這麼多的併發呼叫和併發會話。因此,如果您的網絡應用程序中的每個會話都有一個開放的會話,您將很快達到默認限制並需要更改它,並且性能會受到影響。 檢查瞭解優化這些(和其他)設置的更多信息: http://msdn.microsoft.com/en-us/library/ee377061(v=bts.10).aspx – BrandonZeider 2011-03-31 17:24:19

+0

謝謝。我一直在讀一些關於ChannelFactory的內容,看起來我們將會朝這個方向發展。 – Zannjaminderson 2011-03-31 17:39:40

11

嘗試處理在客戶端代理.Faulted事件,如:

((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted); 

private void client_Faulted(object sender, EventArgs e) 
{ 
    client = new Client(); 
    ((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted); 
} 

應該觸發瞬間通道故障,給你一個機會,重新打開它。

您仍然應該在try-catch塊中將每個調用都包裝爲client方法,甚至可能將其包裝在while()循環中,該循環會重試該調用n次,然後記錄失敗。 EG:

bool succeeded = false; 
int triesLeft = 3; 
while (!succeeded && triesLeft > 0) 
{ 
    triesLeft--; 
    try 
    { 
     client.SomeMethod(); 
     succeeded = true; 
    } 
    catch (exception ex) 
    { 
     logger.Warn("Client call failed, retries left: " + triesLeft; 
    } 
} 
if (!succeeded) 
    logger.Error("Could not call web service"); 

在我的代碼,我儘可能使用ManualResetEvent阻斷while()循環,直到client_Faulted事件處理程序有機會來重新創建client代理了。

+1

退訂'Faulted'事件怎麼樣?應該在哪裏完成? – itsho 2017-09-04 07:52:54