1、WCF中异常阐述:
消息与特定技术有关,.NET异常同样如此,因而WCF并不支持传统的异常处理方式。如果在WCF服务中采用传统的方式处理异常,由于异常消息不能被序列化,因而客户端无法收到服务抛出的异常,为了弥补这一缺陷,WCF会将无法识别的异常均当作为FaultException异常对象,因此,客户端可以捕获FaultException或者Exception异常:
尤为严重的是这样的异常处理方式还会导致传递消息的通道出现错误,当客户端继续调用该服务代理对象的服务操作时,会获得一个CommunicationObjectFaultedException异常,无法继续使用服务。如果服务被设置为PerSession模式或者Single模式,异常还会导致服务对象被释放,终止服务。
2、WCF中的解决方案:
WCF为异常处理专门提供了FaultContract特性,它可以被应用到服务操作上,指明操作可能会抛出的异常类型
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IDocumentsExplorerService
{
[OperationContract]
[FaultContract(typeof(DirectoryNotFoundException))]
DocumentList FetchDocuments(string homeDir);
}
然而,即使通过FaultContract指定了操作要抛出的异常,然而如果服务抛出的异常并非FaultException或者FaultException<T>异常,同样会导致通道发生错误。
因此在服务实现中,正确的实现应该如下:
public class DocumentsExplorerService : IDocumentsExplorerService,IDisposable
{
public DocumentList FetchDocuments(string homeDir)
{
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)));
}
}
}
我们可以将服务所要抛出的异常类型作为FaultException<T>的类型参数,然后创建一个FaultReason对象用以传递错误消息。
3、客户端的调用方式:
客户端在调用服务代理对象时,可以捕获FaultException< DirectoryNotFoundException>异常,并且该异常不会使得通道发生错误,并且客户端可以继续使用该服务代理对象。即使服务为PerCall服务,客户端仍然可以继续调用服务操作。如果服务为Session服务或Singleton服务,那么即使发生了异常,服务对象也不会被终结。
4、其他解决方案:
在发布服务与部署服务时,我们应避免将服务的IncludeExceptionDetailInFaults设置为true,如果不希望使用FaultContract,同时又要保证服务抛出的异常能够被客户端捕获,并且不会导致通道错误,我们还可以通过错误处理扩展的方式实现
第一步:实现相关接口
此时,我们可以将服务本身作为错误处理对象,令其实现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);
}
}
第二步:要使得错误处理扩展生效,还需要向服务通道安装错误处理扩展
方法是让服务类实现System.ServiceModel.Description.IServiceBehavior接口
代码:
public class DocumentsExplorerService : IDocumentsExplorerService,IErrorHandler,IServiceBehavior,IDisposable
{…}
然后在ApplyDispatchBehavior()方法中安装错误处理扩展:
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(this);
}
}
通过这样的处理,即使服务抛出的异常为DirectoryNotFoundException异常,并且在服务契约中没有通过FaultContract特性指定该异常,客户端同样能够获得异常的错误信息,且该异常不会导致通道发生错误,客户端可以继续调用服务代理对象的操作