WCF入门(七)——异常处理1

首先以一个简单的例子演示一下远程调用发生异常的结果:

服务器端代码如下:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void ErrorTest();
    }

    public class Service1 : IService1
    {
        public void ErrorTest() { throw new InvalidOperationException("
异常测试
");
    }

该服务非常简单,直接抛一个异常。当运行客户端代码触发这个异常时,客户端会接收到如下异常:

  

从中我们可以看到:虽然服务器端抛出的是InvalidOperationException异常,但是客户端接收到的却是FaultException异常。从WCF测试客户端的传递的消息中也可以看出这一点。

  

WCF异常处理机制

  1. 通讯异常,这通常是因为链路的原因,比如服务没有启动,网络阻塞等。通常是CommunicationException或者其派生类
  2. 超时异常,这类异常是因为操作时间超时,超时时会抛出 TimeoutException
  3. 服务异常,由服务端根据具体的业务逻辑触发,当业务逻辑抛出异常时,会将其封装成FaultException抛给客户端。

通信异常和超时异常是由WCF框架所产生的,由于网络原因,它们出现是正常的,因此也被称为预期异常,是正常调用的时候需要处理的异常。而FaultException异常则是业务逻辑中异常情况时出现的异常,是由自己实现的代码中产生的,是非预期的异常。具体处理方式根据业务逻辑而定。

但值得注意的是:服务被设置为PerSession模式或者Single模式,服务异常还会导致服务对象被释放而终止服务这个是在网上看到的说明,但我自己试的时候没有挂,从WCF服务对象模型上来分析也不应该会挂,也没有看到MSDN上哪儿有写,待后续确认后再更新

至于为什么要将CLR异常封装成FaultException后抛出,我的理解是:对于异常信息,是序列化后才发布给客户端的,也就是说,客户端也得需要理解这个异常才能反序列化异常信息。因此,客户端必须知道异常的格式,由于WCF的客户端并非只有.net语言才能实现,因此需要发布异常格式,由于CLR的异常比较多,加上还有自定义异常,无法公布所有异常格式,因此,将其统一成FaultException发布,这样客户端才能顺利解析。

了解了这个后,我们就知道InvalidOperationException异常为什么会变成了FaultException异常,但是存在的一个问题是:虽然客户端能感知到调用服务发生了异常,但仍然不知道异常信息,有时无法进行进一步处理。

Debug期间异常信息传递

在前面的异常信息中就有说明:有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults (从 ServiceBehaviorAttribute 或从 <serviceDebug> 配置行为)以便将异常信息发送回客户端。它告诉了我们有两个方式可以传递这个异常信息:

  1. 配置serviceBehaviors,将includeExceptionDetailInFaults设置为true

  

  1. 在服务上的ServiceBehaviorAttribute里面设置IncludeExceptionDetailInFaults

  

这两种方式是等效的,不过建议在配置文件里面使用,统一设置比较方便,也方便统一关闭。

经过这个设置后,再运行客户端代码,这次就可以看到异常信息了:

  

连异常的调用栈也能看到,方便我们进行异常定位。

<StackTrace> WcfService.Service1.ErrorTest() 位置 Service1.cs:行号24
SyncInvokeErrorTest(Object , Object[] , Object[] )

不过,这个在另一方面也暴露了服务器的信息,对于这种网络程序来说是不安全的,因此只建议在Debug的时候使用,从serviceDebug的名字中也可以看出这一点。

构造FaultException

为了安全,前面所说的异常信息获取方式,只能在Debug期间使用,那么,对于Release版本,则需要我们自己手动把运行异常封装成FaultException返回。

    public class Service1 : IService1
    {
        public void ErrorTest()
        {
            try
            {
                throw new InvalidOperationException("
异常测试
");
            }
            catch (Exception e)
            {
                throw new FaultException(e.Message);
            }
        }
    }

这次我们就能获取到异常信息了。

  

对于比较复杂的异常信息,可以自己构造一个FaultException<T>,如下是一个简单的示例:

    [ServiceContract]
    public interface Iservice
    {
        [OperationContract]
        [
FaultContract(typeof(DataAccessFault))]
        void Operation();
    }

    public class Service : Iservice
    {
        public void Operation()
        {
            try
            {
                // Access database …
            }
            catch (DbException e)
            {
                DataAccessFault fault = new DataAccessFault();
                fault.AdditionalDetails = e.Message;
                throw new FaultException<DataAccessFault>(fault);
            }
        }
    }

自己手动构造FaultException的方式比较麻烦,需要在每一个提供的接口中都捕获异常,并重新封装抛出,一来不好看,处理起来也比较麻烦。由于篇幅有限,下篇文章中介绍一个更为简单的方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值