WCF中的异常处理

异常消息与特定技术有关,.NET异常同样如此,因而WCF并不支持传统的异常处理方式。如果在WCF服务中采用传统的方式处理异常,由于异常消息不能被序列化,因而客户端无法收到服务抛出的异常,例如这样的服务设计:

[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.");
   }
}

则客户端在调用如上的服务操作时,如果采用如下的捕获方式是无法获取该异常的:

catch (DirectoryNotFoundException ex)
{
     //handle the exception;
}

为了弥补这一缺陷,WCF会将无法识别的异常均当作为FaultException异常对象,因此,客户端可以捕获FaultException或者Exception异常:

catch (FaultException ex)
{
     //handle the exception;
}
catch (Exception ex)
{
     //handle the exception;
}

然而,这样捕获的异常,却无法识别DirectoryNotFoundException所传递的错误信息。尤为严重的是这样的异常处理方式还会导致传递消 息的通道出现错误,当客户端继续调用该服务代理对象的服务操作时,会获得一个CommunicationObjectFaultedException异 常,无法继续使用服务。如果服务被设置为PerSession模式或者Single模式,异常还会导致服务对象被释放,终止服务。

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)
    {
     //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)));
     }
   }
}

我们可以将服务所要抛出的异常类型作为FaultException<T>的类型参数,然后创建一个FaultReason对象用以传递错误消 息。客户端在调用服务代理对象时,可以捕获FaultException< DirectoryNotFoundException>异常,并且该异常不会使得通道发生错误,并且客户端可以继续使用该服务代理对象。即使服务 为PerCall服务,客户端仍然可以继续调用服务操作。如果服务为Session服务或Singleton服务,那么即使发生了异常,服务对象也不会被 终结。

如果只是为了让客户端获得异常消息,即使不施加FaultContract特性,或者抛出非FaultException异常,我 们也可以通过ServiceBehavior特性,将服务的IncludeExceptionDetailInFaults设置为true(默认为 false),此时,客户端可以捕获抛出的非FaultException异常信息,但该异常仍然会导致通道出现错误。

但是,在发布服务与部署服务时,我们应避免将服务的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);
     }
}

而在该接口的HandleError()方法中,则可以处理异常错误,例如记录日志。

要使得错误处理扩展生效,还需要向服务通道安装错误处理扩展。方法是让服务类实现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特 性指定该异常,客户端同样能够获得异常的错误信息,且该异常不会导致通道发生错误,客户端可以继续调用服务代理对象的操作。









本文转自wayfarer51CTO博客,原文链接:http://blog.51cto.com/wayfarer/280124,如需转载请自行联系原作者

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值