WCF初探-12:WCF客户端异常处理

时间:2021-02-24 08:34:09

前言:

 

  • 当我们打开WCF基础客户端通道(无论是通过显式打开还是通过调用操作自动打开)、使用客户端或通道对象调用操作,或关闭基础客户端通道时,都会在客户端应用程序中出现异常。而我们知道WCF是基于网络的通讯服务,错误异常也是要基于消息传递的,在WCF中提供了一个错误消息处理的类FaultException。接下来,我们看一下如何使用它在客户端处理异常。

 

WCF异常类型:

 

  • 意外异常:意外异常包括灾难性故障(如 OutOfMemoryException)和编程错误(如 ArgumentNullException 或 InvalidOperationException)。通常没有有效的方法来处理意外错误,所以通常不应在调用 WCF 客户端通信方法时捕获这些异常。
  • 预期异常:预期异常包括 TimeoutException、CommunicationException 以及 CommunicationException 的任何派生类。这些异常表明通信过程中出现问题,该问题可以通过中止 WCF 客户端并报告通信故障而得到安全的处理。因为外部因素可能导致任何应用程序中出现这些错误,所以正确的应用程序必须捕获这些异常并在发生异常时进行恢复。

 

WCF客户端异常处理示例:

 

  • 工程结构如下图所示:

  WCF初探-12:WCF客户端异常处理

  • 工程结构说明:
  1. Service:类库程序。定义和实现服务契约,包含Add()和Divide(),即加法和除法运算。

  ICalculator.cs代码如下: 

   WCF初探-12:WCF客户端异常处理WCF初探-12:WCF客户端异常处理
using System.ServiceModel;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Service
{
    [ServiceContract]
    public interface ICalculator
    {
        [OperationContract]
        int Add(int value1, int value2);

        [OperationContract]
        int Divide(int value1, int value2);
    }

}
View Code

  Calculator.cs代码如下: 

   WCF初探-12:WCF客户端异常处理WCF初探-12:WCF客户端异常处理
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Service
{
    public class Calculator:ICalculator
    {
        public int Add(int value1, int value2)
        {
            return value1 + value2;
        }

        public int Divide(int value1, int value2)
        {
            try
            {
                return value1 / value2;
            }
            catch(DivideByZeroException)
            {
                throw new FaultException("除数不能为0");
            }
        }
    }
}
View Code

  2.  Host:控制台应用程序。用来承载服务,添加对于Service程序集的引用后,实现以下代码就可以承载服务。

      Program.cs的代码如下:

   WCF初探-12:WCF客户端异常处理WCF初探-12:WCF客户端异常处理
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;
using System.ServiceModel;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(Calculator)))
            {
                host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); };
                host.Open();
                Console.Read();
            }
        }
    }
}
View Code

  App.config的代码如下:

   WCF初探-12:WCF客户端异常处理WCF初探-12:WCF客户端异常处理
<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        
        <services>
            <service name="Service.Calculator" behaviorConfiguration="mexBehavior">
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:1234/Calculator/"/>
                    </baseAddresses>
                </host>
                <endpoint address="" binding="wsHttpBinding" contract="Service.ICalculator" />
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            </service>
        </services>

        <behaviors>
            <serviceBehaviors>
                <behavior name="mexBehavior">
                    <serviceMetadata httpGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>
View Code

  3.  Client:控制台应用程序。将Host承载服务启动后,客户端程序添加对服务地址http://localhost:1234/Calculator/的引用,将命名空间设置为CalculatorServiceRef,

    接下来我们就可以调用服务呢。Client的Program.cs代码如下:

  WCF初探-12:WCF客户端异常处理

    首先,我们将2、3的代码注释掉,只验证服务异常抛出的结果,我们把Divide的除数设置为0,此时应该会捕获到服务端抛出的异常信息。运行结果如下:

  WCF初探-12:WCF客户端异常处理

   然后,我们把1、3注释,只验证通讯超时的异常抛出。我们将通道连接后的操作时间设置为非常小的一个值,那么服务端的运算肯定来不及处理,就会抛出超

   时的异常信息。运行结果如下:

  WCF初探-12:WCF客户端异常处理

    最后,我们将1、2注释,只验证通讯错误异常信息,我们在客户端执行完Add()后,就把服务终止,即服务终止连接则会抛出通讯错误的异常信息,运行结果如下所示:

  WCF初探-12:WCF客户端异常处理

总结:

 

  • 通过示例,我们可以看到,客户端程序成功捕获到了TimeoutException和CommunicationException以及自定义异常信息
  • 如果发生预期异常,客户端或许可以继续使用,或许无法继续使用。若要确定客户端是否仍然可以使用,请检查 State 属性是否为 CommunicationState.Opened。

    如果此属性仍然处于打开状态,则客户端仍然可以使用。否则,则应中止客户端并释放对其的所有引用。具体参照代码如下:

if (proxy.State == CommunicationState.Opened){
    Console.WriteLine("CommunicationState is Opened");
 }

 

WCF初探-12:WCF客户端异常处理