继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Close和Abort到底该怎么用才对得起观众

慕田峪9129951
关注TA
已关注
手记 337
粉丝 200
获赞 993

 

一:文起缘由

          写这一篇的目的源自于最近看同事在写wcf的时候,用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常,特别感觉奇怪,就比如下面的代码。

复制代码

 1         public void StartNormalMarketing(int shopId, List<int> marketingIdList) 2         { 3  4             using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient()) 5             { 6                 try 7                 { 8  9                     client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing);10 11                 }12                 catch (Exception ex)13                 {14                     LogHelper.WriteLog("常规营销活动开启服务", ex);15                 }16                 finally17                 {18                     try19                     {20                         client.Close();21                     }22                     catch (Exception)23                     {24                         client.Abort();25                     }26                 }27             }28         }

复制代码

看完上面的代码,不知道你是否有什么感想?而且我还问了同事,为什么try catch要写成这样,同事说是根据什么书上来的什么最佳实践,这话一说,我也不敢轻易

怀疑了,只能翻翻源代码看看这话是否有道理,首先我来说说对这段代码的第一感觉。。。

 

1. 代码特别繁琐

  我们写代码,特别不喜欢繁琐,上面的代码就是一例,你try catch就try catch,还在finally中嵌套一个try catch,真的有点感觉像吃了两只癞蛤蟆一样。。。

 

2. 混淆close和abort的用法  

  这种代码给人的感觉就是为什么不精简一下呢???比如下面这样,起码还可以少写一对try catch,对吧。

复制代码

 1         public void StartNormalMarketing(int shopId, List<int> marketingIdList) 2         { 3  4             using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient()) 5             { 6                 try 7                 { 8  9                     client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing);10 11                     client.Close();12                 }13                 catch (Exception ex)14                 {15                     LogHelper.WriteLog("常规营销活动开启服务", ex);16 17                     client.Abort();18                 }19             }20         }

复制代码

而且乍一看这段代码和文中开头那一段代码貌似实现一样,但是某些人的“最佳实践”却不是这样,所以确实会导致我这样的后来人犯迷糊,对吧。。。反正我就是头晕,

简直就是弄糊涂到什么时候该用close,什么时候该用abort。。。

      

二:探索原理

  为了弄明白到底可不可以用一个try catch来替代之,下面我们一起研究一下。

 

1.  从代码注释角度甄别

    从类库的注释中,可以比较有意思的看出,abort方法仅仅比close多一个“立即”,再无其他,有意思,不过这对我来说并没有什么卵用,因为这个注释太

笼统了,为了让自己更加彻底的明白,只能来翻看下close和abort的源代码。

 

2.  从源码角度甄别

  为了方便让ILSpy调试Client代码,现在我决定用ChannelFactory来代替,如下图:

复制代码

 1 namespace ConsoleApplication1 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(); 8  9             try10             {11                 var channel = factory.CreateChannel();12 13                 factory.Close();14             }15             catch (Exception ex)16             {17                 factory.Abort();18             }19         }20     }21 }

复制代码

为了让大家更好的理解,我把close方法的源码提供如下:

复制代码

 1 // System.ServiceModel.Channels.CommunicationObject 2 [__DynamicallyInvokable] 3 public void Close(TimeSpan timeout) 4 { 5     if (timeout < TimeSpan.Zero) 6     { 7         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("timeout", SR.GetString("SFxTimeoutOutOfRange0"))); 8     } 9     using ((DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose) ? this.CreateCloseActivity() : null)10     {11         CommunicationState communicationState;12         lock (this.ThisLock)13         {14             communicationState = this.state;15             if (communicationState != CommunicationState.Closed)16             {17                 this.state = CommunicationState.Closing;18             }19             this.closeCalled = true;20         }21         switch (communicationState)22         {23         case CommunicationState.Created:24         case CommunicationState.Opening:25         case CommunicationState.Faulted:26             this.Abort();27             if (communicationState == CommunicationState.Faulted)28             {29                 throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);30             }31             goto IL_174;32         case CommunicationState.Opened:33         {34             bool flag2 = true;35             try36             {37                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);38                 this.OnClosing();39                 if (!this.onClosingCalled)40                 {41                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);42                 }43                 this.OnClose(timeoutHelper.RemainingTime());44                 this.OnClosed();45                 if (!this.onClosedCalled)46                 {47                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);48                 }49                 flag2 = false;50                 goto IL_174;51             }52             finally53             {54                 if (flag2)55                 {56                     if (DiagnosticUtility.ShouldTraceWarning)57                     {58                         TraceUtility.TraceEvent(TraceEventType.Warning, 524292, SR.GetString("TraceCodeCommunicationObjectCloseFailed", new object[]59                         {60                             this.GetCommunicationObjectType().ToString()61                         }), this);62                     }63                     this.Abort();64                 }65             }66             break;67         }68         case CommunicationState.Closing:69         case CommunicationState.Closed:70             goto IL_174;71         }72         throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState");73         IL_174:;74     }75 }

复制代码

然后我提供一下Abort代码:

复制代码

 1 // System.ServiceModel.Channels.CommunicationObject 2 [__DynamicallyInvokable] 3 public void Abort() 4 { 5     lock (this.ThisLock) 6     { 7         if (this.aborted || this.state == CommunicationState.Closed) 8         { 9             return;10         }11         this.aborted = true;12         this.state = CommunicationState.Closing;13     }14     if (DiagnosticUtility.ShouldTraceInformation)15     {16         TraceUtility.TraceEvent(TraceEventType.Information, 524290, SR.GetString("TraceCodeCommunicationObjectAborted", new object[]17         {18             TraceUtility.CreateSourceString(this)19         }), this);20     }21     bool flag2 = true;22     try23     {24         this.OnClosing();25         if (!this.onClosingCalled)26         {27             throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);28         }29         this.OnAbort();30         this.OnClosed();31         if (!this.onClosedCalled)32         {33             throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);34         }35         flag2 = false;36     }37     finally38     {39         if (flag2 && DiagnosticUtility.ShouldTraceWarning)40         {41             TraceUtility.TraceEvent(TraceEventType.Warning, 524291, SR.GetString("TraceCodeCommunicationObjectAbortFailed", new object[]42             {43                 this.GetCommunicationObjectType().ToString()44             }), this);45         }46     }47 }

复制代码

 

仔细观察完这两个方法,你会发现什么呢???至少我可以提出下面四个问题:

 

1:Abort是Close的子集吗?

   是的,因为如果你看懂了Close,你会发现Close只针对Faulted 和Opened做了判断,而其中在Faulted的枚举下会调用原生的Abort方法。。。如下图

 

2:我能监视Client的各种状态吗?比如Created,Opening,Fault,Closed等等。。。

   当然可以了,wcf的信道老祖宗就是ICommunicationObject,而它就有5种监听事件,这些就可以随时监听,懂伐???

复制代码

 1         static void Main(string[] args) 2         { 3             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie")); 4  5             try 6             { 7                 factory.Opened += (o, e) => 8                 { 9                     Console.WriteLine("Opened");10                 };11 12                 factory.Closing += (o, e) =>13                 {14                     Console.WriteLine("Closing");15                 };16 17                 factory.Closed += (o, e) =>18                 {19                     Console.WriteLine("Closed");20                 };21 22                 var channel = factory.CreateChannel();23 24                 var result = channel.Update(new Student() { });25 26                 factory.Close();27             }28             catch (Exception ex)29             {30                 factory.Abort();31             }32         }

复制代码

 

3:Abort会抛出异常吗?

  

从这个截图中可以看到非常有意思的一段,那就是居然abort活生生的把异常给吞了。。。骨头都不给吐出来。。。真tmd的神奇到家了,想想也有道理,因为只有

这样,我们上层的代码在catch中才不会二次抛出“未处理异常”了,对吧,再转念看一下Close方法。

 

从上面图中可以看到,Close在遇到Faulted之后调用Abort方法,如果说Abort方法调用失败,Close方法会再次判断状态,如果还是Faulted的话,就会向上抛出

异常。。。这就是为什么Abort不会抛异常,Close会的原因,所以Close千万不要放在Catch块中。

 

4. Abort代码大概都干了些什么

  这个问题问的好,要能完美解决的话,我们看下代码,如下图,从图中可以看到,Abort的大目的就是用来关闭信道,具体会经过closeing,abort和closed这

三个方法,同时,这三个事件也会被老祖宗ICommunicationObject监听的到。

 

 

好了,最后我们关注的一个问题在于下面这条语句是否应该放在Try块中???

1  ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie"));

很简单,我们简要的看一下代码,看里面是否会有“异常”抛出即可。。。。

 

可以看到,在new的过程中可能,或许会有异常的产生,所以最好把try catch改成下面这样。。。

复制代码

 1     class Program 2     { 3         static void Main(string[] args) 4         { 5             ChannelFactory<IHomeService> factory = null; 6             try 7             { 8                 factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie")); 9 10                 var channel = factory.CreateChannel();11 12                 var result = channel.Update(new Student() { });13 14                 factory.Close();15 16                 throw new Exception();17             }18             catch (Exception ex)19             {20                 if (factory != null)21                     factory.Abort();22             }23         }24     }

复制代码

 

好了,综合我上面所说的一切,我个人觉得最好的方式应该是上面这样,夜深了,睡觉了,晚安。

 

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP