[转]十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
一:文起缘由
写这一篇的目的源自于最近看同事在写wcf的时候,用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常,特别感觉奇怪,就比如下面的代码。
![](http://common.cnblogs.com/images/copycode.gif)
public void StartNormalMarketing(int shopId, List<int> marketingIdList)
{ using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient())
{
try
{ client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing); }
catch (Exception ex)
{
LogHelper.WriteLog("常规营销活动开启服务", ex);
}
finally
{
try
{
client.Close();
}
catch (Exception)
{
client.Abort();
}
}
}
}
![](http://common.cnblogs.com/images/copycode.gif)
看完上面的代码,不知道你是否有什么感想?而且我还问了同事,为什么try catch要写成这样,同事说是根据什么书上来的什么最佳实践,这话一说,我也不敢轻易
怀疑了,只能翻翻源代码看看这话是否有道理,首先我来说说对这段代码的第一感觉。。。
1. 代码特别繁琐
我们写代码,特别不喜欢繁琐,上面的代码就是一例,你try catch就try catch,还在finally中嵌套一个try catch,真的有点感觉像吃了两只癞蛤蟆一样。。。
2. 混淆close和abort的用法
这种代码给人的感觉就是为什么不精简一下呢???比如下面这样,起码还可以少写一对try catch,对吧。
![](http://common.cnblogs.com/images/copycode.gif)
public void StartNormalMarketing(int shopId, List<int> marketingIdList)
{ using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient())
{
try
{ client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing); client.Close();
}
catch (Exception ex)
{
LogHelper.WriteLog("常规营销活动开启服务", ex); client.Abort();
}
}
}
![](http://common.cnblogs.com/images/copycode.gif)
而且乍一看这段代码和文中开头那一段代码貌似实现一样,但是某些人的“最佳实践”却不是这样,所以确实会导致我这样的后来人犯迷糊,对吧。。。反正我就是头晕,
简直就是弄糊涂到什么时候该用close,什么时候该用abort。。。
二:探索原理
为了弄明白到底可不可以用一个try catch来替代之,下面我们一起研究一下。
1. 从代码注释角度甄别
从类库的注释中,可以比较有意思的看出,abort方法仅仅比close多一个“立即”,再无其他,有意思,不过这对我来说并没有什么卵用,因为这个注释太
笼统了,为了让自己更加彻底的明白,只能来翻看下close和abort的源代码。
2. 从源码角度甄别
为了方便让ILSpy调试Client代码,现在我决定用ChannelFactory来代替,如下图:
![](http://common.cnblogs.com/images/copycode.gif)
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(); try
{
var channel = factory.CreateChannel(); factory.Close();
}
catch (Exception ex)
{
factory.Abort();
}
}
}
}
![](http://common.cnblogs.com/images/copycode.gif)
为了让大家更好的理解,我把close方法的源码提供如下:
![](http://common.cnblogs.com/images/copycode.gif)
// System.ServiceModel.Channels.CommunicationObject
[__DynamicallyInvokable]
public void Close(TimeSpan timeout)
{
if (timeout < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("timeout", SR.GetString("SFxTimeoutOutOfRange0")));
}
using ((DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose) ? this.CreateCloseActivity() : null)
{
CommunicationState communicationState;
lock (this.ThisLock)
{
communicationState = this.state;
if (communicationState != CommunicationState.Closed)
{
this.state = CommunicationState.Closing;
}
this.closeCalled = true;
}
switch (communicationState)
{
case CommunicationState.Created:
case CommunicationState.Opening:
case CommunicationState.Faulted:
this.Abort();
if (communicationState == CommunicationState.Faulted)
{
throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
}
goto IL_174;
case CommunicationState.Opened:
{
bool flag2 = true;
try
{
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
this.OnClosing();
if (!this.onClosingCalled)
{
throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
}
this.OnClose(timeoutHelper.RemainingTime());
this.OnClosed();
if (!this.onClosedCalled)
{
throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
}
flag2 = false;
goto IL_174;
}
finally
{
if (flag2)
{
if (DiagnosticUtility.ShouldTraceWarning)
{
TraceUtility.TraceEvent(TraceEventType.Warning, , SR.GetString("TraceCodeCommunicationObjectCloseFailed", new object[]
{
this.GetCommunicationObjectType().ToString()
}), this);
}
this.Abort();
}
}
break;
}
case CommunicationState.Closing:
case CommunicationState.Closed:
goto IL_174;
}
throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState");
IL_174:;
}
}
![](http://common.cnblogs.com/images/copycode.gif)
然后我提供一下Abort代码:
![](http://common.cnblogs.com/images/copycode.gif)
// System.ServiceModel.Channels.CommunicationObject
[__DynamicallyInvokable]
public void Abort()
{
lock (this.ThisLock)
{
if (this.aborted || this.state == CommunicationState.Closed)
{
return;
}
this.aborted = true;
this.state = CommunicationState.Closing;
}
if (DiagnosticUtility.ShouldTraceInformation)
{
TraceUtility.TraceEvent(TraceEventType.Information, , SR.GetString("TraceCodeCommunicationObjectAborted", new object[]
{
TraceUtility.CreateSourceString(this)
}), this);
}
bool flag2 = true;
try
{
this.OnClosing();
if (!this.onClosingCalled)
{
throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
}
this.OnAbort();
this.OnClosed();
if (!this.onClosedCalled)
{
throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
}
flag2 = false;
}
finally
{
if (flag2 && DiagnosticUtility.ShouldTraceWarning)
{
TraceUtility.TraceEvent(TraceEventType.Warning, , SR.GetString("TraceCodeCommunicationObjectAbortFailed", new object[]
{
this.GetCommunicationObjectType().ToString()
}), this);
}
}
}
![](http://common.cnblogs.com/images/copycode.gif)
仔细观察完这两个方法,你会发现什么呢???至少我可以提出下面四个问题:
1:Abort是Close的子集吗?
是的,因为如果你看懂了Close,你会发现Close只针对Faulted 和Opened做了判断,而其中在Faulted的枚举下会调用原生的Abort方法。。。如下图
2:我能监视Client的各种状态吗?比如Created,Opening,Fault,Closed等等。。。
当然可以了,wcf的信道老祖宗就是ICommunicationObject,而它就有5种监听事件,这些就可以随时监听,懂伐???
![](http://common.cnblogs.com/images/copycode.gif)
static void Main(string[] args)
{
ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie")); try
{
factory.Opened += (o, e) =>
{
Console.WriteLine("Opened");
}; factory.Closing += (o, e) =>
{
Console.WriteLine("Closing");
}; factory.Closed += (o, e) =>
{
Console.WriteLine("Closed");
}; var channel = factory.CreateChannel(); var result = channel.Update(new Student() { }); factory.Close();
}
catch (Exception ex)
{
factory.Abort();
}
}
![](http://common.cnblogs.com/images/copycode.gif)
3:Abort会抛出异常吗?
从这个截图中可以看到非常有意思的一段,那就是居然abort活生生的把异常给吞了。。。骨头都不给吐出来。。。真tmd的神奇到家了,想想也有道理,因为只有
这样,我们上层的代码在catch中才不会二次抛出“未处理异常”了,对吧,再转念看一下Close方法。
从上面图中可以看到,Close在遇到Faulted之后调用Abort方法,如果说Abort方法调用失败,Close方法会再次判断状态,如果还是Faulted的话,就会向上抛出
异常。。。这就是为什么Abort不会抛异常,Close会的原因,所以Close千万不要放在Catch块中。
4. Abort代码大概都干了些什么
这个问题问的好,要能完美解决的话,我们看下代码,如下图,从图中可以看到,Abort的大目的就是用来关闭信道,具体会经过closeing,abort和closed这
三个方法,同时,这三个事件也会被老祖宗ICommunicationObject监听的到。
好了,最后我们关注的一个问题在于下面这条语句是否应该放在Try块中???
ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie"));
很简单,我们简要的看一下代码,看里面是否会有“异常”抛出即可。。。。
可以看到,在new的过程中可能,或许会有异常的产生,所以最好把try catch改成下面这样。。。
![](http://common.cnblogs.com/images/copycode.gif)
class Program
{
static void Main(string[] args)
{
ChannelFactory<IHomeService> factory = null;
try
{
factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie")); var channel = factory.CreateChannel(); var result = channel.Update(new Student() { }); factory.Close(); throw new Exception();
}
catch (Exception ex)
{
if (factory != null)
factory.Abort();
}
}
}
![](http://common.cnblogs.com/images/copycode.gif)
好了,综合我上面所说的一切,我个人觉得最好的方式应该是上面这样,夜深了,睡觉了,晚安。
[转]十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众的更多相关文章
- 十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
一:文起缘由 写这一篇的目的源自于最近看同事在写wcf的时候,用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常,特别感觉奇怪,就比如下面的代码. public void S ...
- 十五天精通WCF——第一天 三种Binding让你KO80%的业务
转眼wcf技术已经出现很多年了,也在.net界混的风生水起,同时.net也是一个高度封装的框架,作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单了, 再简单的话马路上的大妈也能写wcf了 ...
- 十五天精通WCF——第十四天 一起聊聊FaultException
我们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,原因你应该也知道,服务器异常嘛, 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错 ...
- [转]十五天精通WCF——第十四天 一起聊聊FaultException
我们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,原因你应该也知道,服务器异常嘛, 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错 ...
- [转]十五天精通WCF——第一天 三种Binding让你KO80%的业务
转眼wcf技术已经出现很多年了,也在.net界混的风生水起,同时.net也是一个高度封装的框架,作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单了, 再简单的话马路上的大妈也能写wcf了 ...
- 十五天精通WCF——终结篇 那些你需要注意的坑
终于一路走来,到了本系列的最后一篇了,这一篇也没什么好说的,整体知识框架已经在前面的系列文章中讲完了,wcf的配置众多,如果 不加一些指定配置,你可能会遇到一些灾难性的后果,快来一睹为快吧. 一: 第 ...
- 十五天精通WCF——第十三天 用WCF来玩Rest
在我们玩wcf的时候,都会潜意识的觉得wcf就是通过soap协议交换消息的,并且可以在basic,tcp,msmq等等绑定中任意切换, 牛逼的一塌糊涂,但是呢,如果说哪一天wcf不再使用soap协议, ...
- 十五天精通WCF——第十二天 说说wcf中的那几种序列化
我们都知道wcf是由信道栈组成的,在我们传输的参数走到传输信道层之前,先需要经过序列化的过程,也就是将参数序列化为message,这篇 我们就来说说这里的序列化,蛮有意思的,可能初学者也明白,在wcf ...
- 十五天精通WCF——第十一天 如何对wcf进行全程监控
说点题外话,我们在玩asp.net的时候,都知道有一个叼毛玩意叫做“生命周期”,我们可以用httpmodule在先于页面的page_load中 做一些拦截,这样做的好处有很多,比如记录日志,参数过滤, ...
随机推荐
- Android点9图的运用
在Android UI设计开发中,我们经常会用到一些图标.图片来做背景等. 相信很多同学都会遇到一个问题,就是我们让美工做好一张图,一个图标,呃,看起来挺好看的,但是放进app中,扩大或缩小.在不同分 ...
- NS2学习笔记(五)
对无线网络,生成nam文件要使用namtrace-all-wireless, 而不是namtrace-all: set nf [open test_1.nam w] $ns_ namtrace-all ...
- 【USACO2009 Open】滑雪课程ski
[USACO2009 Open]滑雪课程 Ski Lessons Time Limit: 1000 ms Memory Limit: 131072 KBytes Description 约翰请贝西去科 ...
- Patch 21352635 - Database Patch Set Update 11.2.0.4.8
一.CPU和PSU 近日,将数据库从9.2.0.6升级到11.2.0.4后,发现11.2.0.4通过DBLINK访问其他的9i库时发生ORA-02072错误,通过Google找到解决方案,即升级到PS ...
- 338 Counting Bits Bit位计数
给定一个非负整数 num. 对于范围 0 ≤ i ≤ num 中的每个数字 i ,计算其二进制数中的1的数目并将它们作为数组返回.示例:比如给定 num = 5 ,应该返回 [0,1,1,2,1,2] ...
- [转]linux grep命令
转自:http://www.cnblogs.com/end/archive/2012/02/21/2360965.html 1.作用Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表 ...
- js面试笔试题
1. Js的Typeof返回类型有那些? string:undefined:number; function:object:boolean:symbol(ES6) 2. null和undefined的 ...
- JVM 内存区域划分
一.运行时数据区包括哪几部分? 根据<Java虚拟机规范>的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register).Java栈(VM Stac ...
- [ BZOJ 2134 ] 单选错位
\(\\\) \(Description\) 一共\(N\)道题目,第\(i\)道题有\(A_i\)个选项,现在有一个人做完了所有题目,但将每一道题的答案都写到了下一道题的位置\((\)第\( ...
- [ SHOI 2014 ] 概率充电器
\(\\\) \(Description\) 一个含\(N\)个元器件的树形结构充电器,第\(i\)个元器件有\(P_i\)的概率直接从外部被充电,连接\(i,j\)的边有\(P_{i,j}\)的概率 ...