WCF系列教程之WCF客户端异常处理

本文参考自:http://www.cnblogs.com/wangweimutou/p/4414393.html,纯属读书笔记,加深记忆

一、简介

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

二、WCF异常类型

1、意外异常:意外异常包括

(1)、灾难性故障(OutOfMemoryException)

(2)、编程错误(ArgumentException(参数异常)和InvalidOperationException(无效的操作异常))

通常没有有效的方法来处理意外错误,所以通产不应该在调用WCF客户端时捕获这些异常

2、预期异常:预期异常包括

(1)、TimeoutException

(2)、CommunicationException 

(3)、CommunicationException 的任何派生类

上面这些异常表明在通信的过程中出现问题,该问题可以通过终止WCF客户端并报告通信故障而得到安全的处理,因为外部因素可能导致任何应用程序中出现这些错误,所以正确的应用程序必须捕获这些异常并在发生异常时进行恢复。

(4)、如果发生预期异常,客户端或许可以继续使用,或许无法继续使用。若要确定客户端是否仍然可以使用,请检查 State 属性是否为 CommunicationState.Opened。如果此属性仍然处于打开状态,则客户端仍然可以使用。否则,则应中止客户端并释放对其的所有引用。具体参照代码如下:

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

三、代码示例

工程结构如下图所示:

1、WCF服务层搭建:新建契约层、服务层、和WCF宿主,添加必须的引用(这里不会的参考本人前面的随笔),配置宿主,生成解决方案,打开Host.exe,开启服务。具体的代码如下:

ICalculate.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace IService
{
    [ServiceContract]
    public interface ICalculate
    {
        [OperationContract]
        int Add(int a, int b);

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

IUserInfo.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace IService
{
    [ServiceContract]
    public interface IUserInfo
    {
        [OperationContract]
        User[] GetInfo(int? id);
    }

    [DataContract]
    public class User
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public int Age { get; set; }
        [DataMember]
        public string Nationality { get; set; }  
    }
}

注:必须引入System.Runtime.Serialization命名空间,应为User类在被传输时必须是可序列化的,否则将无法传输

Calculate.cs

using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;

namespace Service
{
    public class Calculate : ICalculate
    {
        public int Add(int a, int b)
        {
            return a + b;
        }


        public int Divide(int a, int b)
        {
            try
            {
                return a / b;
            }
            catch (DivideByZeroException) {
                throw new FaultException("除数不能为0");//FaultException需要引用System.ServiceModel命名空间
            }
        }
    }
}

UserInfo.cs

using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Service
{
    public class UserInfo : IUserInfo
    {
        public User[] GetInfo(int? id)
        {
            List<User> Users = new List<User>();
            Users.Add(new User { ID = 1, Name = "张三", Age = 11, Nationality = "China" });
            Users.Add(new User { ID = 2, Name = "李四", Age = 12, Nationality = "English" });
            Users.Add(new User { ID = 3, Name = "王五", Age = 13, Nationality = "American" });

            if (id != null)
            {
                return Users.Where(x => x.ID == id).ToArray();
            }
            else
            {
                return Users.ToArray();
            }
        }
    }
}

Program.cs

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(Calculate)))
            {
                host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); };
                host.Open();
                Console.Read();
            }
        }
    }
}

App.Config

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>

    <services>
      <service name="Service.Calculate" behaviorConfiguration="mexBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/Calculate/"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="wsHttpBinding" contract="IService.ICalculate" />
        <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>

ok,打开Host.exe

服务开启成功!

2、新建一个名为Client的控制台应用程序作为WCF客户端,添加对http://localhost:1234/Calculate/的引用,将命名空间设置为CalculateClientNS,

点击确定,确保添加成功。然后开始编写Program.cs的代码

(1)、验证除数不能为0的异常抛出

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using Client.CalculateClientNS;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CalculateClient calculate = new CalculateClient();
                Console.WriteLine("1+2={0}", calculate.Add(1, 2));
                Console.WriteLine("1/0={0}", calculate.Divide(1, 0));
            }
            catch (TimeoutException ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
            catch (CommunicationException ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
        }
    }
}

客户端接收到了服务器返回的除数不能为0的异常,然后抛出。

(2)、验证通讯超时的异常抛出,原理通过将连接后的时间设置为很小的值,那么服务端的运算肯定来不及,就会抛出超时的信息。

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using Client.CalculateClientNS;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CalculateClient calculate = new CalculateClient();
                calculate.InnerChannel.OperationTimeout = TimeSpan.FromSeconds(0.001);//设置下面调用的操作必须在0.001秒内完成.
                Console.WriteLine("1+2={0}", calculate.Add(1, 2));
            }
            catch (TimeoutException ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
            catch (CommunicationException ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
        }
    }
}

验证通讯超时的异常抛出

(3)、验证通讯错误的异常抛出

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using Client.CalculateClientNS;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CalculateClient calculate = new CalculateClient();
                calculate.Abort();//关闭通道
                Console.WriteLine("1+2={0}", calculate.Add(1, 2));
            }
            catch (TimeoutException ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
            catch (CommunicationException ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType() + ":" + ex.Message);
                Console.ReadKey();
            }
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏博客园

Asp.Net Web API(六)

     Asp.Net Web API不可以需要IIS。可以自己在主机上承载一个Web API

15240
来自专栏跟着阿笨一起玩NET

ASP.NET多线程下使用HttpContext.Current为null解决方案

前面我还提到在APM模式下的异步完成回调时,访问HttpContext.Current也会返回null,那么此时该怎么办呢?

19020
来自专栏张善友的专栏

使用MongoDB的支持Linq 驱动NoRM

MongoDB的驱动有好几个,分布式文件存储的数据库开源项目MongoDB里使用的是github.com/samus/mongodb-csharp,monogo...

226100
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构

前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多。主要集中增删该查每个模块都有,所以本次是为封装相同接口方法    如果你...

33060
来自专栏hbbliyong

WCF发布多个服务

using System; using System.Collections.Generic; using System.Linq; using System....

39750
来自专栏马洪彪

C#获取AD域中计算机和用户的信息

如果你的计算机加入了某个AD域,则可以获取该域中所有的计算机和用户的信息。 所用程序集,需要.Net Framework 4。 添加程序集引用 System.D...

39470
来自专栏菩提树下的杨过

几种异步操作方式

其实这也是面试中被问倒的问题:(贴在这里纪念一下,注:只是简单的罗列,详细原理及分析,请参阅《CLR Via c#》第三版相关章节) 1、利用线程池发起异步操...

19760
来自专栏菩提树下的杨过

利用fluorineFx将DataTable从.Net传递到Flash

FluorineFx自带的示例都不错,就是有点不简洁,下面的代码基本上已经最简版了(环境vs2010) 1、先创建一个Web Application,然后添加F...

24950
来自专栏salesforce零基础学习

salesforce 零基础学习(三十三)通过REST方式访问外部数据以及JAVA通过rest方式访问salesforce

本篇参考Trail教程: https://developer.salesforce.com/trailhead/force_com_dev_intermedia...

68080
来自专栏恰童鞋骚年

ASP.Net请求处理机制初步探索之旅 - Part 2 核心

开篇:上一篇我们了解了一个请求从客户端发出到服务端接收并转到ASP.Net处理入口的过程,这篇我们开始探索ASP.Net的核心处理部分,借助强大的反编译工具,我...

9620

扫码关注云+社区

领取腾讯云代金券