zoukankan      html  css  js  c++  java
  • 正确捕获 WCF服务调用中发生的异常及处理技巧

    概述:本节主要讲述在服务调用中可能发生的异常及模拟异常的发生,并分析何时可捕获何种异常,以及如何把服务异常以正确的方式传递到客户端,

              文章最后给出正确捕获异常的捕获顺序。本次异常捕获仅为介绍,部分为应用性功能,所以代码和行文相对简单,还介绍了在服务器端异常处理的一些技巧。

    1、  首先,我们创建一个简单的计算器服务器和客户端,如下:

     

    点击展开代码
    //服务器
    [ServiceContract]
    publicinterface ICalc
    {
    [OperationContract]
    [FaultContract(
    typeof(GreentingError))]
    string Div(int x, int y);
    }
    publicclass Calc : ServiceBase, ICalc
    {
    publicstring Div(int x, int y)
    {
    string result =string.Empty;
    try
    {result
    =string.Format("result: {0}", x / y); } catch (DivideByZeroException ex)
    {
    throw ex; }return result; }}
    //客户端
    [ServiceContract]
    publicinterface ICalc
    {
    [OperationContract]
    [FaultContract(
    typeof(GreentingError))]
    string Div(int x, int y);
    }
    publicclass CalcClient : ClientBase<ICalc>, ICalc
    {
    publicstring Div(int x, int y) {returnbase.Channel.Div(x, y); }}

    好吧,我承认代码相当的简单,不过我喜欢简洁的东西。

     

    2、 简单的东西就是好,调用都简单得多;我们来调用一下。      

    try
    {
    CalcClientcalcclient
    =new CalcClient();
    string result =calcclient.Div(10, 0);
    Console.WriteLine(result);
    Console.ReadKey();
    }
    catch (TimeoutExceptionex) {throw ex; }
    catch (FaultException<GreentingError> ex) {throw ex; }
    catch (FaultExceptionex) {throw ex;
    catch (System.ServiceModel.CommunicationException ex) {throw ex; }
    catch (Exceptionex) {throw ex; }

     

    3、当我们在调用服务的Div(int x,int y)方法并给对数y传递了值为0后,服务器端将会引发DivideByZeroException的异常,这在预料之中。这时候,

    在客户端的FaultException部分捕获了这个异常。

     

    4、没问题,我们再在服务器代码中手动抛出FaultException异常。

     

    catch (DivideByZeroException ex)
    {FaultException exception
    =new FaultException(ex.Message); throw exception;}

    这时候发现,还是FaultException捕获了这个异常,为何?

     

    5、再做一个测试。

    在服务加入这句代码:System.Threading.Thread.Sleep(70000);使得服务超时。

    这回终于是TimeOutException捕获了服务器的异常,那么我们就要问了,FaultException< GreentingError>何时会捕获异常呢?答案是当服务器抛出FaultException< GreentingError>的时候,引用MSDN上的一段话(绿色部分):

    如果侦听器接收到操作协定中不期望或未指定的 SOAP 错误,将会引发 FaultException对象, 可以发送两种类型的 SOAP 错误:已声明的和未声明的。 已声明的 SOAP 错误是指其中的某个操作具有System.ServiceModel.FaultContractAttribute属性(用于指定自定义 SOAP 错误类型)的错误。 未声明的 SOAP 错误是在操作的协定中没有指定的错误。这里的“不期望或未指定的 SOAP 错误”是指未在服务操作中应用FaultContractAttribute包装的自定义错误类型。

     

    6、那么何时会捕获CommincationException异常呢?

    MSDN上说是:应用程序处理在通信期间可能会引发的 CommunicationException 对象

    好吧,为了引发这个异常,我们来作如下操作。首先在服务器关闭当前通道对象。

     

    OperationContext.Current.Channel.Close();

    很遗憾,客户端并没有捕获到CommunicationException,而是捕获到了TimeOutException异常!因为服务通道关闭后,并未发生异常,所以没有返回消息到客户端,客户端在等待一定时间后,超时退出。

    所以我们在关闭通道的同时指定一个TimeSpan。这样可以让调用立即返回,当然,还可以通过Channel.Abort来完成调用返回。

     

    OperationContext.Current.Channel.Close(new TimeSpan(5000));

    在调用了IContextChannel的Close方法的同时,指定在超时前必须完成发送操作的时间,这样可以使得消息在指定时间内立即返回,而不必等到服务调用超时,否则到时客户端必将引发TimeOutException异常,而不是CommunicationException异常。

    7、补救措施

    同时,为了在服务出现异常时我们可以采取一些补救的措施,我们新建了一个抽象类ServiceBase,并使得Calc服务实现类继承自它,这样我们就可以在服务各种状态转换中取得控制权。ServiceBase类如下:

     

    publicabstractpartialclass ServiceBase
    {
    private IContextChannel channel =null;
    protected ServiceBase()
    {
    channel
    = OperationContext.Current.Channel;
    channel.Opening
    +=new EventHandler(delegate(object sender, EventArgs e) {/* TO DO*/ });
    channel.Opened
    +=new EventHandler(delegate(object sender, EventArgs e) {/* TO DO*/ });
    channel.Closing
    +=new EventHandler(delegate(object sender, EventArgs e) {/* TO DO*/ });
    channel.Closed
    +=new EventHandler(delegate(object sender, EventArgs e) { Abort(); });
    channel.Faulted
    +=new EventHandler(delegate(object sender, EventArgs e) { Abort(); });
    }
    void Open() {/* TO DO*/ }
    void Close() { /* TO DO*/}
    void Abort() { channel.Abort(); }
    }

    从上面的代码中可以看出,在服务通道关闭以后,我们立即将服务中止,让消息立即返回,这时候即使在操作中关闭了服务而又未指定超时完成的时间,调用依然可以立即返回。

    这次客户端总算捕获到了CommunicationException异常,见下图:

    为何会这样?

     

    8、让我们来看一下CommunicationException的继承层次,从中我们可以得到启示。

    8.1、首先是FaultException<TDetail>的继承层次。

    8.2、再次是TimeOutException的继承层次。

    9、从上图中可以看出,TimeOutException和CommunicationException均继承自SystemException类,而FaultException继承自CommunicationException,最后是FaultException<TDetail>继承自FaultException类。

    10、最后我们得出,在客户端正确的捕获异常的顺序应该是:

    TimeOutException> FaultException<TDetail> > FaultException >CommunicationException> Exception。在这里强烈建议开发人员抛出和捕获FaultException<TDetail>类型的异常。

  • 相关阅读:
    我是如何折腾.NET Resx资源文件的 当计算机中的资源已经足够多时,我们也要学会尽可能的借用
    当程序开发人员开始抛弃技术时,是否意味着噩梦的开始?抛弃了SQL Server 2000才发现客户的简单问题真的很难解决
    分享.NET ERP项目开发中应用到的重量级工具 选择合适的工具和资源,做项目效率高而且规范程度高
    Management Console ERP项目开发辅助工具 正确的方法+适当的工具使做项目的效率高而且问题少
    ERP系统管理员的工具箱 推荐几款优秀的数据比较同步工具 Data Compare and Sync tool
    亲自下载CSDN社区600万用户数据 设计两条编程题目考验你的.NET编程基础
    知识管理系统Data Solution研发日记之十六 保存服务器文档为本机PDF格式
    【转】好的学习方法
    iPhone开发学习笔记[7/50]在xcode里配置成功subversion
    iPhone开发学习笔记[4/50]表视图的使用
  • 原文地址:https://www.cnblogs.com/viter/p/1646810.html
Copyright © 2011-2022 走看看