[Wcf] 發生"ServiceChannel 處於 Faulted 狀態,因此無法用來通訊"的解決辦法
前言
最近專案在使用Wcf來當作網路通訊的基礎架構,
但在開發時期常常出現下面這個問題,
ServiceChannel 處於 Faulted 狀態,因此無法用來通訊
這時候Client無法Dispose,也無法呼叫任何Service Function,
只能把Client Abort掉再重新建立,
現在讓我們來了解一下問題發生的原因吧!
實際演練
首先,讓我們來重建問題發生的場景,
一開始先建立一個Wcf的Service
IDivideService
public interface IDivideService
{
[OperationContract]
int Divide(int a, int b);
}
DevideService
{
#region IDivideService Members
public int Divide(int a, int b)
{
return a / b;
}
#endregion
}
再來建立Wcf的Client
{
public DivideServiceProxy()
{
}
#region IDivideService Members
public int Divide(int a, int b)
{
return base.Channel.Divide(a, b);
}
#endregion
}
我們在程式中使用Client的片段如下
{
try
{
mProxy.Divide(1, 0);
}
catch(Exception e)
{
//Do Error Handling
}
}
執行之後我們可以看到,程式抓到錯誤訊息如下:
這是很正常的,因為我們嘗試除以零,所以我們可以接收到Server所傳出的Exception,
但接下來跑完Using區段之後,卻發現在mProxy Dispose階段出現下列Exception而無法Dispose
我們雖然有用Try Catch包住了,卻還是造成Channel變成Faulted的狀況, 這是因為在Wcf中,
若Service擲出任何未定義在FaultContract中的Exception,Channel就會切換為Faulted的狀態!
解決的方法很簡單,我們只要在Service Contract中定義我們預期會發生並允許Client接收的FaultContract即可解決,
首先在Interface中定義FaultContract
public interface IDevideService
{
[OperationContract]
[FaultContract(typeof(CalculateError))]
int Divide(int a, int b);
}
[DataContract]
public class CalculateError
{
public CalculateError(string errorMessage)
{
this.ErrorMessage = errorMessage;
}
[DataMember]
public string ErrorMessage { get; set; }
}
改寫Service,當DivideByZero時丟出定義好的FaultException
{
#region IDevideService Members
public int Divide(int a, int b)
{
int result;
try
{
result = a / b;
}
catch (DivideByZeroException ex)
{
throw new FaultException<CalculateError>(new CalculateError(ex.Message), new FaultReason("Can't Divide by Zero!"));
}
catch
{
throw;
}
return result;
}
#endregion
}
再次重新執行我們的Client程式,我們可以發現能夠正確地接受到Exception,Channel也不會變成Faulted的狀態了
當然在開發過程中,我們可能並不會在開發初期就將所有預期發生的Exception完全定義在FaultContract中,
在Client開發時也可以使用一種比較Tricky的解決方法,但建議在開發後期FaultContract定義明確之後,
要把這段程式碼拿掉,使用上面介紹的方法來避免這個問題的發生!使用正確的錯誤處理機制
我們可以在Client的Proxy Class中註冊Channel的Faulted Event,並即時的建立一個新的頻道
{
(base.Channel as ICommunicationObject).Faulted += new EventHandler(DivideServiceProxy_Faulted);
}
void DivideServiceProxy_Faulted(object sender, EventArgs e)
{
(sender as ICommunicationObject).Abort();
if (sender is IDivideService)
{
sender = base.ChannelFactory.CreateChannel();
}
}
當Channel變成Faulted的時候,我們就馬上Dispose並創建一個新的Channel來替代,
就可以讓Client在Debug的過程不會一直被中斷囉!
結語
在Wcf的開發之中,所有Client會收到的Exception應該都是明確定義在FaultContract之中的,
若有任何未被定義在FaultContract之中的Exception被擲出,就會造成Channel變成Faulted的狀況發生,
我們應該在程式之中盡可能地考慮的各種Exception的發生情況,並定義清楚允許Client收到的FaultContract。
如果沒有處理好也可能會洩漏系統的安全漏洞,需特別小心注意才行!
如果有任何問題歡迎大家多多提出來討論 ^_^