十五天精通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     }

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

时间: 2024-10-23 19:54:44

十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众的相关文章

十五天精通WCF——第四天 你一定要明白的通信单元Message

原文:十五天精通WCF--第四天 你一定要明白的通信单元Message 转眼你已经学了三天的wcf了,是不是很好奇wcf在传输层上面到底传递的是个什么鸟毛东西呢???应该有人知道是soap,那soap这叼毛长得是什么 样呢?这一篇我们来揭开答案... 一:soap到底长成什么样子 为了能看清soap长的啥样,我可以用强大的Fiddler来监视一下,突然好激动啊!!! 1.Server 1 static void Main(string[] args) 2 { 3 ServiceHost host

十五天精通WCF——第一天 三种Binding让你KO80%的业务

原文:十五天精通WCF--第一天 三种Binding让你KO80%的业务 转眼wcf技术已经出现很多年了,也在.net界混的风生水起,同时.net也是一个高度封装的框架,作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单了, 再简单的话马路上的大妈也能写wcf了,好了,wcf最基本的概念我们放在后面慢慢分析,下面我们来看看神奇的3个binding如何KO我们实际场景中的80%的业务场景. 一:basicHttpBinding 作为入门第一篇,也就不深入谈谈basic中的信道栈中那些啥

十五天精通WCF——第九天 高级玩法之自定义Behavior

原文:十五天精通WCF--第九天 高级玩法之自定义Behavior 终于我又看完了二期爱情保卫战,太酸爽了,推荐链接:http://www.iqiyi.com/a_19rrgublqh.html?vfm=2008_aldbd,不多说,谁看谁入迷,下面言归正传, 看看这个很有意思的Behavior. 一: Behavior这个泼妇的厉害   在前面的文章中,我也清楚的说明了整个wcf通信流,而Behavior这个泼妇可以在wcf通信流中的任何地方插上一脚,蛮狠无比,利用的好,让你上天堂,利用的不

十五天精通WCF——第十四天 一起聊聊FaultException

 我们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,原因你应该也知道,服务器异常嘛, 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错误返回到客户端,就比如下面这样. 从这张图中,我故意输入了xss字符,然后的然后,web程序自爆异常,其实我想表达的意思就是,虽然说web程序抛异常了,但不代表iis就 挂了,所以iis还是需要给客户端做出反馈,这就有了http header,和body信息,同样的道理,wcf的服务器异常机制也是这

十五天精通WCF——第十二天 说说wcf中的那几种序列化

我们都知道wcf是由信道栈组成的,在我们传输的参数走到传输信道层之前,先需要经过序列化的过程,也就是将参数序列化为message,这篇 我们就来说说这里的序列化,蛮有意思的,可能初学者也明白,在wcf中默认的序列化是DataContractSerializer,确实是这样,不过wcf在信道中 其实不仅仅支持DataContractSerializer,它还支持其他类型的序列化,比如XmlSerializer,NetDataContractSerializer以及DataContractJson

十五天精通WCF——第十一天 如何对wcf进行全程监控

说点题外话,我们在玩asp.net的时候,都知道有一个叼毛玩意叫做“生命周期”,我们可以用httpmodule在先于页面的page_load中 做一些拦截,这样做的好处有很多,比如记录日志,参数过滤,全局登录验证等等...在wcf里面的话也是有类似的功能,第一种就是在 endpoint中加上runtime的behavior,这样的话就可以先于“服务方法”做拦截,第二种方法呢,也就是我们这一篇所说的全程监控,俗称 ”诊断功能”. 一:诊断 我也说了,“诊断”这是wcf的一个专业术语,意思也就是监控

十五天精通WCF——第六天 你必须要了解的3种通信模式

wcf已经说到第六天了,居然还没有说到这玩意有几种通信模式,惭愧惭愧,不过很简单啦,单向,请求-响应,双工模式,其中的第二种“请求-响应“ 模式,这个大家不用动脑子都清楚,这一篇我大概来分析下. 一:“请求-响应“模式   如果你看了我上一篇的博文,你应该非常清楚这种类似“本地调用”的方式,wcf同样也分为“同步”和“异步”两种,不过不管是异步还是同步,最终都逃 不过是“请求-响应”这个事实,对吧. 1: 同步方式 这种方式我想没什么好说的,前面几篇我已经说的非常清楚了,具体使用方法可以参考我的

十五天精通WCF——第十天 学会用SvcConfigEditor来简化配置

我们在玩wcf项目的时候,都是自己手工编写system.serviceModel下面的配置,虽然在webconfig中做wcf的服务配置的时候,vs提供大多 数的代码提示,但对于不太熟悉服务配置的小鸟们来说,有些困难,而且一些服务配置也容易遗漏,大多情况下,我们都是copy一份服务配置,然 后在服务配置上面修修改改,对吧...其实呢,.net给我们提供了一个强大的scvconfigeditor这个工具化的软件来帮助我们生成wcf的配置,是 不是很神奇??? 一:工具在何处 当然在无比牛逼的Mic

十五天精通WCF——第二天 告别烦恼的config配置

经常搞wcf的基友们肯定会知道,当你的应用程序有很多的“服务引用”的时候,是不是有一种疯狂的感觉...从一个环境迁移到另外一个环境,你需要改变的 endpoint会超级tmd的多,简直就是搞死了人...好了,这篇我们来看看如何最小化配置. 一:精简service的config配置 就像上一篇的代码一样,我的service端的config配置如下: 1 <?xml version="1.0" encoding="utf-8" ?> 2 <config