WCF中的异常处理
異常消息與特定技術(shù)有關(guān),.NET異常同樣如此,因而WCF并不支持傳統(tǒng)的異常處理方式。如果在WCF服務(wù)中采用傳統(tǒng)的方式處理異常,由于異常消息不能被序列化,因而客戶端無(wú)法收到服務(wù)拋出的異常,例如這樣的服務(wù)設(shè)計(jì):
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IDocumentsExplorerService
{
??? [OperationContract]?????? ?
??? DocumentList FetchDocuments(string homeDir);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class DocumentsExplorerService : IDocumentsExplorerService,IDisposable
{
public DocumentList FetchDocuments(string homeDir)
{
??? //Some Codes
?
??? if (Directory.Exists(homeDir))
??? {
??????? //Fetch documents according to homedir
??? }
??? else
??? {
??????? throw new DirectoryNotFoundException(
string.Format("Directory {0} is not found.",homeDir));
??? }
}
public void Dispose()
{?????????? ?
??? Console.WriteLine("The service had been disposed.");
}
}
則客戶端在調(diào)用如上的服務(wù)操作時(shí),如果采用如下的捕獲方式是無(wú)法獲取該異常的:
catch (DirectoryNotFoundException ex)
{
??? //handle the exception;
}
為了彌補(bǔ)這一缺陷,WCF會(huì)將無(wú)法識(shí)別的異常均當(dāng)作為FaultException異常對(duì)象,因此,客戶端可以捕獲FaultException或者Exception異常:
catch (FaultException ex)
{
??? //handle the exception;
}
catch (Exception ex)
{
??? //handle the exception;
}
然而,這樣捕獲的異常,卻無(wú)法識(shí)別DirectoryNotFoundException所傳遞的錯(cuò)誤信息。尤為嚴(yán)重的是這樣的異常處理方式還會(huì)導(dǎo)致傳遞消息的通道出現(xiàn)錯(cuò)誤,當(dāng)客戶端繼續(xù)調(diào)用該服務(wù)代理對(duì)象的服務(wù)操作時(shí),會(huì)獲得一個(gè)CommunicationObjectFaultedException異常,無(wú)法繼續(xù)使用服務(wù)。如果服務(wù)被設(shè)置為PerSession模式或者Single模式,異常還會(huì)導(dǎo)致服務(wù)對(duì)象被釋放,終止服務(wù)。
WCF為異常處理專門提供了FaultContract特性,它可以被應(yīng)用到服務(wù)操作上,指明操作可能會(huì)拋出的異常類型。例如前面的服務(wù)契約就可以修改為:
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IDocumentsExplorerService
{
[OperationContract]
[FaultContract(typeof(DirectoryNotFoundException))]
??? DocumentList FetchDocuments(string homeDir);
}
然而,即使通過(guò)FaultContract指定了操作要拋出的異常,然而如果服務(wù)拋出的異常并非FaultException或者FaultException<T>異常,同樣會(huì)導(dǎo)致通道發(fā)生錯(cuò)誤。因此在服務(wù)實(shí)現(xiàn)中,正確的實(shí)現(xiàn)應(yīng)該如下:
public class DocumentsExplorerService : IDocumentsExplorerService,IDisposable
{
public DocumentList FetchDocuments(string homeDir)
{
??? //Some Codes
?
??? if (Directory.Exists(homeDir))
??? {
??????? //Fetch documents according to homedir
??? }
??? else
??? {
??????? DirectoryNotFoundException exception = new DirectoryNotFoundException();
??????? throw new FaultException<DirectoryNotFoundException>(exception,
new FaultReason(string.Format("Directory {0} is not found.", homeDir)));
??? }
}
}
我們可以將服務(wù)所要拋出的異常類型作為FaultException<T>的類型參數(shù),然后創(chuàng)建一個(gè)FaultReason對(duì)象用以傳遞錯(cuò)誤消息。客戶端在調(diào)用服務(wù)代理對(duì)象時(shí),可以捕獲FaultException< DirectoryNotFoundException>異常,并且該異常不會(huì)使得通道發(fā)生錯(cuò)誤,并且客戶端可以繼續(xù)使用該服務(wù)代理對(duì)象。即使服務(wù)為PerCall服務(wù),客戶端仍然可以繼續(xù)調(diào)用服務(wù)操作。如果服務(wù)為Session服務(wù)或Singleton服務(wù),那么即使發(fā)生了異常,服務(wù)對(duì)象也不會(huì)被終結(jié)。
如果只是為了讓客戶端獲得異常消息,即使不施加FaultContract特性,或者拋出非FaultException異常,我們也可以通過(guò)ServiceBehavior特性,將服務(wù)的IncludeExceptionDetailInFaults設(shè)置為true(默認(rèn)為false),此時(shí),客戶端可以捕獲拋出的非FaultException異常信息,但該異常仍然會(huì)導(dǎo)致通道出現(xiàn)錯(cuò)誤。
但是,在發(fā)布服務(wù)與部署服務(wù)時(shí),我們應(yīng)避免將服務(wù)的IncludeExceptionDetailInFaults設(shè)置為true。
如果不希望使用FaultContract,同時(shí)又要保證服務(wù)拋出的異常能夠被客戶端捕獲,并且不會(huì)導(dǎo)致通道錯(cuò)誤,我們還可以通過(guò)錯(cuò)誤處理擴(kuò)展的方式實(shí)現(xiàn)。此時(shí),我們可以將服務(wù)本身作為錯(cuò)誤處理對(duì)象,令其實(shí)現(xiàn)System.ServiceModel.Dispatcher.IErrorHandler接口:
public class DocumentsExplorerService : IDocumentsExplorerService,IErrorHandler, IDisposable
{…}
在該接口的ProvideFault()方法中,可以將非FaultContract異常提升為FaultContract<T>異常,例如將DirectoryNotFoundException異常提升為FaultExceptino<DirectoryNotFoundException>異常:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
??? if (error is DirectoryNotFoundException)
??? {
??????? FaultException<DirectoryNotFoundException> faultException = new FaultException<DirectoryNotFoundException>(
??????????? new DirectoryNotFoundException(), new FaultReason(error.Message));
??????? MessageFault messageFault = faultException.CreateMessageFault();
??????? fault = Message.CreateMessage(version,messageFault,faultException.Action);
??? }
}
而在該接口的HandleError()方法中,則可以處理異常錯(cuò)誤,例如記錄日志。
要使得錯(cuò)誤處理擴(kuò)展生效,還需要向服務(wù)通道安裝錯(cuò)誤處理擴(kuò)展。方法是讓服務(wù)類實(shí)現(xiàn)System.ServiceModel.Description.IServiceBehavior接口:
public class DocumentsExplorerService : IDocumentsExplorerService,IErrorHandler,IServiceBehavior,IDisposable
{…}
然后在ApplyDispatchBehavior()方法中安裝錯(cuò)誤處理擴(kuò)展:
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
??? foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
??? {
??????? dispatcher.ErrorHandlers.Add(this);
??? }
}
通過(guò)這樣的處理,即使服務(wù)拋出的異常為DirectoryNotFoundException異常,并且在服務(wù)契約中沒(méi)有通過(guò)FaultContract特性指定該異常,客戶端同樣能夠獲得異常的錯(cuò)誤信息,且該異常不會(huì)導(dǎo)致通道發(fā)生錯(cuò)誤,客戶端可以繼續(xù)調(diào)用服務(wù)代理對(duì)象的操作。
本文已在IT168發(fā)表:http://tech.it168.com/msoft/2007-12-03/200712031939709.shtml
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IDocumentsExplorerService
{
??? [OperationContract]?????? ?
??? DocumentList FetchDocuments(string homeDir);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class DocumentsExplorerService : IDocumentsExplorerService,IDisposable
{
public DocumentList FetchDocuments(string homeDir)
{
??? //Some Codes
?
??? if (Directory.Exists(homeDir))
??? {
??????? //Fetch documents according to homedir
??? }
??? else
??? {
??????? throw new DirectoryNotFoundException(
string.Format("Directory {0} is not found.",homeDir));
??? }
}
public void Dispose()
{?????????? ?
??? Console.WriteLine("The service had been disposed.");
}
}
則客戶端在調(diào)用如上的服務(wù)操作時(shí),如果采用如下的捕獲方式是無(wú)法獲取該異常的:
catch (DirectoryNotFoundException ex)
{
??? //handle the exception;
}
為了彌補(bǔ)這一缺陷,WCF會(huì)將無(wú)法識(shí)別的異常均當(dāng)作為FaultException異常對(duì)象,因此,客戶端可以捕獲FaultException或者Exception異常:
catch (FaultException ex)
{
??? //handle the exception;
}
catch (Exception ex)
{
??? //handle the exception;
}
然而,這樣捕獲的異常,卻無(wú)法識(shí)別DirectoryNotFoundException所傳遞的錯(cuò)誤信息。尤為嚴(yán)重的是這樣的異常處理方式還會(huì)導(dǎo)致傳遞消息的通道出現(xiàn)錯(cuò)誤,當(dāng)客戶端繼續(xù)調(diào)用該服務(wù)代理對(duì)象的服務(wù)操作時(shí),會(huì)獲得一個(gè)CommunicationObjectFaultedException異常,無(wú)法繼續(xù)使用服務(wù)。如果服務(wù)被設(shè)置為PerSession模式或者Single模式,異常還會(huì)導(dǎo)致服務(wù)對(duì)象被釋放,終止服務(wù)。
WCF為異常處理專門提供了FaultContract特性,它可以被應(yīng)用到服務(wù)操作上,指明操作可能會(huì)拋出的異常類型。例如前面的服務(wù)契約就可以修改為:
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IDocumentsExplorerService
{
[OperationContract]
[FaultContract(typeof(DirectoryNotFoundException))]
??? DocumentList FetchDocuments(string homeDir);
}
然而,即使通過(guò)FaultContract指定了操作要拋出的異常,然而如果服務(wù)拋出的異常并非FaultException或者FaultException<T>異常,同樣會(huì)導(dǎo)致通道發(fā)生錯(cuò)誤。因此在服務(wù)實(shí)現(xiàn)中,正確的實(shí)現(xiàn)應(yīng)該如下:
public class DocumentsExplorerService : IDocumentsExplorerService,IDisposable
{
public DocumentList FetchDocuments(string homeDir)
{
??? //Some Codes
?
??? if (Directory.Exists(homeDir))
??? {
??????? //Fetch documents according to homedir
??? }
??? else
??? {
??????? DirectoryNotFoundException exception = new DirectoryNotFoundException();
??????? throw new FaultException<DirectoryNotFoundException>(exception,
new FaultReason(string.Format("Directory {0} is not found.", homeDir)));
??? }
}
}
我們可以將服務(wù)所要拋出的異常類型作為FaultException<T>的類型參數(shù),然后創(chuàng)建一個(gè)FaultReason對(duì)象用以傳遞錯(cuò)誤消息。客戶端在調(diào)用服務(wù)代理對(duì)象時(shí),可以捕獲FaultException< DirectoryNotFoundException>異常,并且該異常不會(huì)使得通道發(fā)生錯(cuò)誤,并且客戶端可以繼續(xù)使用該服務(wù)代理對(duì)象。即使服務(wù)為PerCall服務(wù),客戶端仍然可以繼續(xù)調(diào)用服務(wù)操作。如果服務(wù)為Session服務(wù)或Singleton服務(wù),那么即使發(fā)生了異常,服務(wù)對(duì)象也不會(huì)被終結(jié)。
如果只是為了讓客戶端獲得異常消息,即使不施加FaultContract特性,或者拋出非FaultException異常,我們也可以通過(guò)ServiceBehavior特性,將服務(wù)的IncludeExceptionDetailInFaults設(shè)置為true(默認(rèn)為false),此時(shí),客戶端可以捕獲拋出的非FaultException異常信息,但該異常仍然會(huì)導(dǎo)致通道出現(xiàn)錯(cuò)誤。
但是,在發(fā)布服務(wù)與部署服務(wù)時(shí),我們應(yīng)避免將服務(wù)的IncludeExceptionDetailInFaults設(shè)置為true。
如果不希望使用FaultContract,同時(shí)又要保證服務(wù)拋出的異常能夠被客戶端捕獲,并且不會(huì)導(dǎo)致通道錯(cuò)誤,我們還可以通過(guò)錯(cuò)誤處理擴(kuò)展的方式實(shí)現(xiàn)。此時(shí),我們可以將服務(wù)本身作為錯(cuò)誤處理對(duì)象,令其實(shí)現(xiàn)System.ServiceModel.Dispatcher.IErrorHandler接口:
public class DocumentsExplorerService : IDocumentsExplorerService,IErrorHandler, IDisposable
{…}
在該接口的ProvideFault()方法中,可以將非FaultContract異常提升為FaultContract<T>異常,例如將DirectoryNotFoundException異常提升為FaultExceptino<DirectoryNotFoundException>異常:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
??? if (error is DirectoryNotFoundException)
??? {
??????? FaultException<DirectoryNotFoundException> faultException = new FaultException<DirectoryNotFoundException>(
??????????? new DirectoryNotFoundException(), new FaultReason(error.Message));
??????? MessageFault messageFault = faultException.CreateMessageFault();
??????? fault = Message.CreateMessage(version,messageFault,faultException.Action);
??? }
}
而在該接口的HandleError()方法中,則可以處理異常錯(cuò)誤,例如記錄日志。
要使得錯(cuò)誤處理擴(kuò)展生效,還需要向服務(wù)通道安裝錯(cuò)誤處理擴(kuò)展。方法是讓服務(wù)類實(shí)現(xiàn)System.ServiceModel.Description.IServiceBehavior接口:
public class DocumentsExplorerService : IDocumentsExplorerService,IErrorHandler,IServiceBehavior,IDisposable
{…}
然后在ApplyDispatchBehavior()方法中安裝錯(cuò)誤處理擴(kuò)展:
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
??? foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
??? {
??????? dispatcher.ErrorHandlers.Add(this);
??? }
}
通過(guò)這樣的處理,即使服務(wù)拋出的異常為DirectoryNotFoundException異常,并且在服務(wù)契約中沒(méi)有通過(guò)FaultContract特性指定該異常,客戶端同樣能夠獲得異常的錯(cuò)誤信息,且該異常不會(huì)導(dǎo)致通道發(fā)生錯(cuò)誤,客戶端可以繼續(xù)調(diào)用服務(wù)代理對(duì)象的操作。
本文已在IT168發(fā)表:http://tech.it168.com/msoft/2007-12-03/200712031939709.shtml
轉(zhuǎn)載于:https://www.cnblogs.com/wayfarer/archive/2007/12/24/1012142.html
總結(jié)
- 上一篇: sql server 2005 T-SQ
- 下一篇: TCSH编程(2)