WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境下的重要意义,并阐明了其根本原因。但是,是否直接调用ICommunicationObject的Close方法将服务代理关闭就万事大吉了呢?事情远不会这么简单,这其中还会涉及关于异常处理的一些操作,这就是本篇文章需要讨论的话题。
一、异常的抛出与Close的失败
一般情况下,当服务端抛出异常,客户客户端的服务代理不能直接关闭,WCF在执行Close方法的过程中会抛出异常。我们可以通过下面的例子来证实这一点。在这个例子中,我们依然沿用计算服务的例子,下面是服务契约和服务实现的定义:
1: using System.ServiceModel;
2: namespace Artech.ExceptionHandlingDemo.Contracts
3: {
4: [ServiceContract(Namespace = "urn:artech.com")]
5: public interface ICalculator
6: {
7: [OperationContract]
8: int Divide(int x, int y);
9: }
10: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: using System;
2: using System.ServiceModel;
3: using Artech.ExceptionHandlingDemo.Contracts;
4: namespace Artech.ExceptionHandlingDemo.Services
5: {
6: class CalcualtorService : ICalculator { public int Divide(int x, int y) { return x / y; } }
7: }
为了确保服务代理的及时关闭,按照典型的编程方式,我们需要采用try/catch/finally的方式才操作服务代理对象,并把服务代理的关闭放在finally块中。WCF服务在客户端的调用程序如下所示:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: using System;
2: using System.ServiceModel;
3: using Artech.ExceptionHandlingDemo.Contracts;
4: namespace Client
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: using (ChannelFactory<ICalculator> channelFatory = new ChannelFactory<ICalculator>(new WSHttpBinding(), "http://127.0.0.1:3721/calculatorservice"))
11: {
12: ICalculator calcultor = channelFatory.CreateChannel(); try
13: {
14: calcultor.Divide(1, 0);
15: }
16: catch (Exception ex) { Console.WriteLine(ex.Message); }
17: finally
18: {
19: (calcultor as ICommunicationObject).Close();
20: }
21: }
22: }
23: }
24: }
由于传入的参数为1和0,在服务执行除法运算的时候,会抛出DividedByZero的异常。当服务端程序执行到finally块中对服务代理进行关闭的时候,会抛出如下一个CommunicationObjectFaultedException异常,提示SerivceChannel的状态为Faulted,不能用于后续Communication。
![]()
二、原理分析
要解释具体的原因,还得从信道(Channel)的两种分类形式说起。在上面一篇文章中,我们就谈到过:WCF通过信道栈实现了消息的编码、传输及基于某些特殊功能对消息的特殊处理,而绑定对象是信道栈的缔造者,不同的绑定类型创建出来的信道栈具有不同的特性。就对会话的支持来讲,我们可以将信道分为以下两种:
- 会话信道(Sessionful Channel):会话信道确保客户端和服务端之间传输的消息能够相互关联,但是信道的错误(Fault)会影响后续的消息交换;
- 数据报信道(Datagram Channel):即使在同一个数据报信道中,每次消息的交换都是相互独立,信道的错误也不会影响后续的消息交换。
由于上面的例子中,我们采用了WsHttpBinding,所以在默认条件下创建的信道(Channel)是会话信道(Sessionful Channel)。异常抛出后,当前信道的状态将变成Faulted,表示信道出现错误。错误的信道将不能继续用于后续的通信,即使是调用Close方法试图将其关闭也不行。
也就是说异常导致信道错误(Faulted)的特性仅仅对于会话信道而言,而对于数据报信道,则没有这样的问题。对于WsHttpBinding在如下两种情况下下具有创建会话信道的能力:
- 采用任何一种非None的SecurityMode
- 采用ReliableSession
再默认的情况下,WsHttpBinding采用的SecurityMode为Message,所以其创建的信道是会话信道。如果我们将其SecurityMode设为None,则在执行Close方法的时候则不会抛出任何异常(而实际上,服务代理的关闭与否对于数据报信道来讲,没有任何意义)。具体实现如下面的代码所示:
1: using System;
2: using System.ServiceModel;
3: using Artech.ExceptionHandlingDemo.Contracts;
4: namespace Client
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: using (ChannelFactory<ICalculator> channelFatory = new ChannelFactory<ICalculator>(new WSHttpBinding(SecurityMode.None), "http://127.0.0.1:3721/calculatorservice"))
11: { //......
12: }
13: }
14: }
15: }
16:
三、Close() V.S. Abort()
在这种情况下,一般会调用另一个方法:Abort,强行中断当前信道。一般情况下,对于客户端来说,信道在下面两种情况下状态会变成Faulted:
- 调用超时,抛出TimeoutException
- 调用失败,抛出CommunicationException
所以正确的客户端进行服务调用的代码应该如下面的代码所示:通过try/catch控制服务调用,在try控制块中进行正常服务调用并正常关闭服务代理进程(调用Close方法);在catch控制块中,捕获CommunicationException和TimeoutException这两个异常,并将服务代理对象强行关闭(调用Abort方法)。下面的代码演示了基于ChannelFactory<T>创建服务代理的WCF客户端编程方式,对于直接通过强类型服务代理(继承ClientBase<T>的服务代理类型)进行服务调用具有相同的结构。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculateservice"))
2: {
3: ICalculator calculator = channelFactory.CreateChannel();
4: try
5: {
6: int result = calculator.Divide(1, 0);
7: (calculator as ICommunicationObject).Close();
8: }
9: catch (CommunicationException ex)
10: {
11: //Exception Handling
12: (calculator as ICommunicationObject).Abort();
13: }
14: catch (TimeoutException ex)
15: {
16: //Exception Handling
17: (calculator as ICommunicationObject).Abort();
18: }
19: catch (Exception ex)
20: {
21: //Exception Handling
22: }
23: }
四、通过一些编程技巧避免重复代码
如果严格按中上面的编程方式对CommunicationException和TimeoutException进出捕获和处理,那么你的客户端代码就会到处充斥中相同的代码片断。我不知一次说过,如果你的代码中重复频率过高,或者编程人员广泛地采用Ctrl+C|Ctrl+V这样的编程方式,那么这就是你进行代码重构的信号。
为此,我们可以通过对Delegate的利用来进行代码的分离(服务调用代码和异常处理代码)。比如,我写了下面两个Invoke方法:
1: using System;
2: using System.ServiceModel;
3: using Artech.ExceptionHandlingDemo.Contracts;
4: namespace Client
5: {
6: class Program
7: {
8: static void Invoke<TContract>(TContract proxy, Action<TContract> action)
9: {
10: try
11: {
12: action(proxy);
13: (proxy as ICommunicationObject).Close();
14: }
15: catch (CommunicationException)
16: {
17: (proxy as ICommunicationObject).Abort();
18: //Handle Exception
19: throw;
20: }
21: catch (TimeoutException )
22: {
23: (proxy as ICommunicationObject).Abort();
24: //Handle Exception
25: throw;
26: }
27: catch (Exception)
28: {
29: //Handle Exception
30: (proxy as ICommunicationObject).Close();
31: }
32: }
33:
34: static TReturn Invoke<TContract, TReturn>(TContract proxy, Func<TContract, TReturn> func)
35: {
36: TReturn returnValue = default(TReturn);
37: try
38: {
39: returnValue = func(proxy);
40: }
41: catch (CommunicationException)
42: {
43: (proxy as ICommunicationObject).Abort();
44: //Handle Exception
45: throw;
46: }
47: catch (TimeoutException)
48: {
49: (proxy as ICommunicationObject).Abort();
50: //Handle Exception
51: throw;
52: }
53: catch (Exception)
54: {
55: //Handle Exception
56: }
57:
58: return returnValue;
59: }
60: }
61: }
那么,对CalculatorService就可以简化成:
1: using System;
2: using System.ServiceModel;
3: using Artech.ExceptionHandlingDemo.Contracts;
4: namespace Client
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: using (ChannelFactory<ICalculator> channelFatory = new ChannelFactory<ICalculator>(new WSHttpBinding(), "http://127.0.0.1:3721/calculatorservice"))
11: {
12: ICalculator calcultor = channelFatory.CreateChannel(); int result = Invoke<ICalculator, int>(calcultor, proxy => proxy.Divide(2, 1)); //......
13: }
14: }
15: }
16: }
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理的更多相关文章
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]
原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇] 在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术 ...
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇] 在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道:当遇到某些异常,我们需要强行中止(Abor ...
- WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)
原文:WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话 ...
- WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化
原文:WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制 ...
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
- WCF技术剖析之十六:数据契约的等效性和版本控制
原文:WCF技术剖析之十六:数据契约的等效性和版本控制 数据契约是对用于交换的数据结构的描述,是数据序列化和反序列化的依据.在一个WCF应用中,客户端和服务端必须通过等效的数据契约方能进行有效的数据交 ...
- WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...
- WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)
原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济 ...
- WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]
原文:WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇] 在[第2篇]中,我们深入剖析了单调(PerCall)模式下WCF对服务实例生命周期的控制,现在我们来 ...
随机推荐
- cocos2dx进阶学习之CCAction
CCAction在cocos2dx中,抽象了节点的动作.它由CCObject派生,因此它不是渲染节点. 它经常的用法是,创建一个action,然后让某个CCNode对象(一般是精灵),去执行它. 在马 ...
- stm32之USART通信
任何USART通信,需要用到2个对外连接的引脚:RxD,TxD: RxD是输入引脚,用于串行数据接收: TxD是输出引脚,用于串行数据发送: SCLK引脚:发生器时钟输出(同步模式下,异步模式下不需要 ...
- 【Web】CGI与Servlet技术对比
CGI:Common Gateway Interface,通用网关接口. 1.CGI处理步骤 首先,客户端(即Web浏览器)根据某资源的URL向Web服务器提出请求:Web服务器的守护进程(HTTP ...
- BZOJ 1006: [HNOI2008]神奇的国度( MCS )
弦图最小染色...先用MCS求出完美消除序列然后再暴力染色... ------------------------------------------------------------------- ...
- Eclipse开启与关闭代码自动提示功能
Eclipse代码里面的代码提示功能默认是关闭的,只有输入“.”的时候才会提示功能,用vs的用户可能不太习惯 这种,vs是输入任何字母都会提示,下面说一下如何修改eclipse配置,开启代码自 ...
- 浏览器与服务器间的交互(客服端 <---> 服务器)
浏览器与服务器间的交互(客服端 <---> 服务器) 请求--->处理--->响应 对类HttpContext 内部成员的使用 例如 :Request .Response . ...
- POJ 1159 回文LCS滚动数组优化
详细解题报告可以看这个PPT 这题如果是直接开int 5000 * 5000 的空间肯定会MLE,优化方法是采用滚动数组. 原LCS转移方程 : dp[i][j] = dp[i - 1][j] + ...
- iOS字符串NSString中去掉空格(或替换为某个字符串)
http://blog.sina.com.cn/s/blog_6f29e81f0101qwbk.html [问题描述] 今天请求服务器返回的字段中含有空格,这空格是服务器开发人员不小心往数 ...
- Git 配置editor编辑器
Git 配置editor编辑器 在ubuntu系统下,Git默认的编辑器是命令行,学名叫V什么的,使用起来诸多不便 在编辑提交日志的时候,用的比较多. 可以选择unbuntu默认的文档编辑器作为git ...
- Windows Phone 8初学者开发—第19部分:设置RecordAudio.xaml页面
原文 Windows Phone 8初学者开发—第19部分:设置RecordAudio.xaml页面 原文地址: http://channel9.msdn.com/Series/Windows-Ph ...