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

简介:

一:文起缘由

写这一篇的目的源自于最近看同事在写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块中???

1 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();
 }
 }
 }

好了,综合我上面所说的一切,我个人觉得最好的方式应该是上面这样.

相关文章
十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
原文:十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众   一:文起缘由           写这一篇的目的源自于最近看同事在写wcf的时候,用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常,特别感觉奇怪,就比如下面的代码。
966 0
|
前端开发
WCF更新服务引用报错的原因之一
WCF更新服务引用报错的原因之一
|
C# 数据安全/隐私保护
c#如何创建WCF服务到发布(SqlServer版已经验证)
c#如何创建WCF服务到发布(SqlServer版已经验证)
87 0
|
安全 数据库连接 数据库
WCF服务创建到发布(SqlServer版)
在本示例开始之前,让我们先来了解一下什么是wcf? wcf有哪些特点? wcf是一个面向服务编程的综合分层架构。该架构的项层为服务模型层。 使用户用最少的时间和精力建立自己的软件产品和外界通信的模型。它使得开发者能够建立一个跨平台的安全、可信赖、事务性的解决方案。且能与已有系统兼容写作。 简单概括就是:一组数据通信的应用程序开发接口。
143 0
|
C++
WCF基础教程(二)——解析iis8和iis8.5+VS2013发布wcf服务问题
WCF基础教程(二)——解析iis8和iis8.5+VS2013发布wcf服务问题
162 0
WCF基础教程(二)——解析iis8和iis8.5+VS2013发布wcf服务问题
WCF使用纯代码的方式进行服务寄宿
服务寄宿的目的是为了开启一个进程,为WCF服务提供一个运行的环境。通过为服务添加一个或者多个终结点,使之暴露给潜在的服务消费,服务消费者通过匹配的终结点对该服务进行调用,除去上面的两种寄宿方式,还可以以纯代码的方式实现服务的寄宿工作。
908 0
|
Windows
WCF服务寄宿到IIS
一.WCF简介: Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台。整合了原有的windows通讯的 .net Remoting,WebService,Socket的机制,并融合有HTTP和FTP的相关技术。
1112 0
WCF服务自我寄宿
WCF服务的寄宿方式 WCF寄宿方式是一种非常灵活的操作,可以寄宿在各种进程之中,常见的寄宿有: IIS服务、Windows服务、Winform程序、控制台程序中进行寄宿,从而实现WCF服务的运行,为调用者方便、高效提供服务调用。
1051 0
|
网络架构
(纯代码)快速创建wcf rest 服务
因为有一个小工具需要和其它的业务对接数据,所以就试一下看能不能弄一个无需配置快速对接的方法出来,百(以)度(讹)过(传)后(讹),最后还是对照wcf配置对象调试出来了: 1.创建WebHttpBinding 2.
1025 0