十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
2015-07-20 09:55
417 查看
原文:十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
一:文起缘由
写这一篇的目的源自于最近看同事在写wcf的时候,用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常,特别感觉奇怪,就比如下面的代码。
看完上面的代码,不知道你是否有什么感想?而且我还问了同事,为什么try catch要写成这样,同事说是根据什么书上来的什么最佳实践,这话一说,我也不敢轻易
怀疑了,只能翻翻源代码看看这话是否有道理,首先我来说说对这段代码的第一感觉。。。
1. 代码特别繁琐
我们写代码,特别不喜欢繁琐,上面的代码就是一例,你try catch就try catch,还在finally中嵌套一个try catch,真的有点感觉像吃了两只癞蛤蟆一样。。。
2. 混淆close和abort的用法
这种代码给人的感觉就是为什么不精简一下呢???比如下面这样,起码还可以少写一对try catch,对吧。
而且乍一看这段代码和文中开头那一段代码貌似实现一样,但是某些人的“最佳实践”却不是这样,所以确实会导致我这样的后来人犯迷糊,对吧。。。反正我就是头晕,
简直就是弄糊涂到什么时候该用close,什么时候该用abort。。。
二:探索原理
为了弄明白到底可不可以用一个try catch来替代之,下面我们一起研究一下。
1. 从代码注释角度甄别
从类库的注释中,可以比较有意思的看出,abort方法仅仅比close多一个“立即”,再无其他,有意思,不过这对我来说并没有什么卵用,因为这个注释太
笼统了,为了让自己更加彻底的明白,只能来翻看下close和abort的源代码。
2. 从源码角度甄别
为了方便让ILSpy调试Client代码,现在我决定用ChannelFactory来代替,如下图:
为了让大家更好的理解,我把close方法的源码提供如下:
然后我提供一下Abort代码:
仔细观察完这两个方法,你会发现什么呢???至少我可以提出下面四个问题:
1:Abort是Close的子集吗?
是的,因为如果你看懂了Close,你会发现Close只针对Faulted 和Opened做了判断,而其中在Faulted的枚举下会调用原生的Abort方法。。。如下图
2:我能监视Client的各种状态吗?比如Created,Opening,Fault,Closed等等。。。
当然可以了,wcf的信道老祖宗就是ICommunicationObject,而它就有5种监听事件,这些就可以随时监听,懂伐???
3:Abort会抛出异常吗?
从这个截图中可以看到非常有意思的一段,那就是居然abort活生生的把异常给吞了。。。骨头都不给吐出来。。。真tmd的神奇到家了,想想也有道理,因为只有
这样,我们上层的代码在catch中才不会二次抛出“未处理异常”了,对吧,再转念看一下Close方法。
从上面图中可以看到,Close在遇到Faulted之后调用Abort方法,如果说Abort方法调用失败,Close方法会再次判断状态,如果还是Faulted的话,就会向上抛出
异常。。。这就是为什么Abort不会抛异常,Close会的原因,所以Close千万不要放在Catch块中。
4. Abort代码大概都干了些什么
这个问题问的好,要能完美解决的话,我们看下代码,如下图,从图中可以看到,Abort的大目的就是用来关闭信道,具体会经过closeing,abort和closed这
三个方法,同时,这三个事件也会被老祖宗ICommunicationObject监听的到。
好了,最后我们关注的一个问题在于下面这条语句是否应该放在Try块中???
很简单,我们简要的看一下代码,看里面是否会有“异常”抛出即可。。。。
可以看到,在new的过程中可能,或许会有异常的产生,所以最好把try catch改成下面这样。。。
好了,综合我上面所说的一切,我个人觉得最好的方式应该是上面这样,夜深了,睡觉了,晚安。
一:文起缘由
写这一篇的目的源自于最近看同事在写wcf的时候,用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常,特别感觉奇怪,就比如下面的代码。
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(); } } } }
看完上面的代码,不知道你是否有什么感想?而且我还问了同事,为什么try catch要写成这样,同事说是根据什么书上来的什么最佳实践,这话一说,我也不敢轻易
怀疑了,只能翻翻源代码看看这话是否有道理,首先我来说说对这段代码的第一感觉。。。
1. 代码特别繁琐
我们写代码,特别不喜欢繁琐,上面的代码就是一例,你try catch就try catch,还在finally中嵌套一个try catch,真的有点感觉像吃了两只癞蛤蟆一样。。。
2. 混淆close和abort的用法
这种代码给人的感觉就是为什么不精简一下呢???比如下面这样,起码还可以少写一对try catch,对吧。
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(); } } }
而且乍一看这段代码和文中开头那一段代码貌似实现一样,但是某些人的“最佳实践”却不是这样,所以确实会导致我这样的后来人犯迷糊,对吧。。。反正我就是头晕,
简直就是弄糊涂到什么时候该用close,什么时候该用abort。。。
二:探索原理
为了弄明白到底可不可以用一个try catch来替代之,下面我们一起研究一下。
1. 从代码注释角度甄别
从类库的注释中,可以比较有意思的看出,abort方法仅仅比close多一个“立即”,再无其他,有意思,不过这对我来说并没有什么卵用,因为这个注释太
笼统了,为了让自己更加彻底的明白,只能来翻看下close和abort的源代码。
2. 从源码角度甄别
为了方便让ILSpy调试Client代码,现在我决定用ChannelFactory来代替,如下图:
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(); } } } }
为了让大家更好的理解,我把close方法的源码提供如下:
// 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, 524292, 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:; } }
然后我提供一下Abort代码:
// 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, 524290, 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, 524291, SR.GetString("TraceCodeCommunicationObjectAbortFailed", new object[] { this.GetCommunicationObjectType().ToString() }), this); } } }
仔细观察完这两个方法,你会发现什么呢???至少我可以提出下面四个问题:
1:Abort是Close的子集吗?
是的,因为如果你看懂了Close,你会发现Close只针对Faulted 和Opened做了判断,而其中在Faulted的枚举下会调用原生的Abort方法。。。如下图
2:我能监视Client的各种状态吗?比如Created,Opening,Fault,Closed等等。。。
当然可以了,wcf的信道老祖宗就是ICommunicationObject,而它就有5种监听事件,这些就可以随时监听,懂伐???
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(); } }
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改成下面这样。。。
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(); } } }
好了,综合我上面所说的一切,我个人觉得最好的方式应该是上面这样,夜深了,睡觉了,晚安。
相关文章推荐
- 0034 类和对象
- .net 枚举(Enum)使用总结
- 火狐浏览器l无法启动
- 通过网页集中访问Docker容器
- 常用函数(二)在现有元素后插入新的元素
- 微信错误提示code= -4/微信发送被拒绝
- Python的Django框架中从url中捕捉文本的方法
- Twemproxy源码分析之一 入口函数及启动过程
- eclipse 改颜色
- 2015_07_20_正式踏入ACM
- UVA140 - Bandwidth
- c#后台验证
- UITextField的代理方法
- Chome v42 支持Java
- MySQL(2):SQL语言的分类
- 程序员的能力拓展模型
- 将ppt转成pdf合适的转换器
- 修改任务栏的任务按钮(任务栏标签)的显示宽度
- Java中throws和throw的区别讲解
- Windows/Linux环境下模拟服务端口方法