zoukankan      html  css  js  c++  java
  • 十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众

    一:文起缘由

              写这一篇的目的源自于最近看同事在写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                 finally
    17                 {
    18                     try
    19                     {
    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             try
    10             {
    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             try
    36             {
    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             finally
    53             {
    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     try
    23     {
    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     finally
    38     {
    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     }
    复制代码

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

  • 相关阅读:
    织梦DedeCms网站首页不生成html文件动态显示方法
    PHP7.0下安装DEDE织梦 出现 GD不支持的解决方法
    DEDECMS5.7支持伪静态的方法
    DEDECMS全站伪静态设置方法
    设置 SSH 通过密钥登录
    Windows安装OpenSSH服务
    VS Code远程开发工具错误-找不到SSH安装
    帝国CMS自定义列表的排序
    帝国CMS灵动标签e:loop的使用方法
    cisco 3750交换机堆叠后配置恢复方法
  • 原文地址:https://www.cnblogs.com/Alex80/p/8900086.html
Copyright © 2011-2022 走看看