这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性。具体来说,这是一个关于如何自动为服务接口(契约)的每个操作添加FaultContract与WCF服务元数据发布的问题。接下来通过一个简单的实例来说明这个因为少写了一行代码引发的血案。

一、手工添加FaultContract

WCF采用基于消息的通信方式,Endpoint的ABC三要素之一的契约(Contract)的本质就是定义消息的结构。契约不仅定义了正常请求和响应负载的结构,还定义了承载异常信息的响应消息的结构。为了让契约能够响应消息承载的错误信息,承载错误信息的类型需要利用FaultContractAttribute特性注册到服务接口的操作方法上。

   1: [ServiceContract]

   2: public interface IMyService

   3: {

   4:     [OperationContract]

   5:     [FaultContract(typeof(ServiceExceptionInfo))]

   6:     string GetData(int value);

   7: }

   8:  

   9: public class MyService : IMyService

  10: {

  11:     public string GetData(int value)

  12:     {

  13:         var ex = new InvalidOperationException("Invalid operation...");

  14:         throw new FaultException<ServiceExceptionInfo>(new ServiceExceptionInfo(ex));

  15:     }

  16: }

  17:  

  18: [DataContract]

  19: public class ServiceExceptionInfo

  20: {

  21:     [DataMember]

  22:     public string ExceptionType { get; set; }

  23:  

  24:     [DataMember]

  25:     public string Message { get; set; }

  26:     public ServiceExceptionInfo(Exception ex)

  27:     {

  28:         this.ExceptionType = ex.GetType().AssemblyQualifiedName;

  29:         this.Message = ex.Message;

  30:     }

  31: }

如下面的代码片段所示,由于GetData操作抛出的FaultException对象采用一个ServiceExceptionInfo来描述详细错误信息,所以我们在定义服务接口的时候需要利用FaultContractAttribute将ServiceExceptionInfo这个类型注册到GetData方法上。

二、利用自定义ServiceHost自动注册ServiceExceptionInfo类型

如果多个操作都需要注册这么一个ServiceExceptionInfo类型,这其实是一件很繁琐的事情。对于今天找我们作技术支持的那个项目来说,由于采用了我们提供的一个自动化异常处理框架,要求所有的操作都需要注册一个类似于ServiceExceptionInfo的类型来描述最终的错误消息。为了让具体的项目可以不用在每个操作上都添加一个FaultContractAttribute,我们自定义了一个ServiceHost来实现了对它的自动注册。如下所示的MyServiceHost模拟了FaultContract自动化注册的逻辑。

   1: public class MyServiceHost: ServiceHost

   2: {

   3:     public MyServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)

   4:     { }

   5:  

   6:     protected override void OnOpening()

   7:     {

   8:         base.OnOpening();

   9:         foreach (var endpoint in this.Description.Endpoints)

  10:         {

  11:             string ns = endpoint.Contract.Namespace.TrimEnd('/');

  12:             foreach (var op in endpoint.Contract.Operations)

  13:             {

  14:                 if (!op.Faults.Any(it => it.DetailType == typeof(ServiceExceptionInfo)))

  15:                 {

  16:                     FaultDescription fault = new FaultDescription($"{ns}/{op.Name}_ServiceExceptionInfo");

  17:                     fault.DetailType = typeof(ServiceExceptionInfo);

  18:                     op.Faults.Add(fault);

  19:                 }

  20:             }

  21:         }

  22:     }

  23: }

  24:  

  25: public class MyServiceHostFactory : ServiceHostFactory

  26: {

  27:     protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)

  28:     {

  29:         return new MyServiceHost(serviceType, baseAddresses);

  30:     }

  31: }

MyServiceHostFactory是MyServiceHost对应的工厂,我们可以采用如下的配置使用它。

   1: <system.serviceModel>

   2:   <behaviors>

   3:     <serviceBehaviors>

   4:       <behavior>

   5:         <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>

   6:         <serviceDebug includeExceptionDetailInFaults="true"/>

   7:       </behavior>

   8:     </serviceBehaviors>

   9:   </behaviors>

  10:   <services>

  11:     <service name="WcfService.MyService">

  12:       <endpoint binding="basicHttpBinding" contract="WcfService.IMyService"/>

  13:     </service>

  14:   </services>

  15:   <serviceHostingEnvironment >

  16:     <serviceActivations>

  17:       <add service="WcfService.MyService" relativeAddress="myservice.svc" factory="WcfService.MyServiceHostFactory"/>

  18:     </serviceActivations>

  19:   </serviceHostingEnvironment>    

  20: </system.serviceModel>

三、获取元数据(WSDL)受阻

在真的WCF服务调用过程中,我们定义的这个MyServiceHost和MyServiceHostFactory一点问题都没有。但是一旦我们利用HTTP-GET获取元数据(WSDL)的时候,会发生如下所示的NullReferenceException异常。

四、一行代码解决这个问题

由于自定义的这个MyServiceHost的代码实在太简单,我实在想不到那个地方导致WsdlExporter的CreateWsdlOperationFault方法(根据Stacktrace,这个异常是从这个方法中抛出来的)。没有办法,只有看WCF的源代码了,这个过程是很痛苦的,因为涉及的代码太多,而且根本不知道这个Null Reference究竟是哪个变量。

既然查看源代码并没有真正解决这个问题,我们还得从自定义的这个MyServiceHost上找原因。这个MyServiceHost的作用简单明了,就是为所有的操作添加一个针对ServiceExceptionInfo类型的FaultDescription对象而已,那么是不是因为添加的FaultDescription对象缺少了某些属性导致的这个异常呢?为此,我将FaultDescription的所有属性都进行了设置,最终发现只要按照如下的方式设置它的Name属性就可以了。

   1: public class MyServiceHost: ServiceHost

   2: {

   3:     public MyServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)

   4:     { }

   5:  

   6:     protected override void OnOpening()

   7:     {

   8:         base.OnOpening();

   9:         foreach (var endpoint in this.Description.Endpoints)

  10:         {

  11:             string ns = endpoint.Contract.Namespace.TrimEnd('/');

  12:             foreach (var op in endpoint.Contract.Operations)

  13:             {

  14:                 if (!op.Faults.Any(it => it.DetailType == typeof(ServiceExceptionInfo)))

  15:                 {

  16:                     FaultDescription fault = new FaultDescription($"{ns}/{op.Name}_ServiceExceptionInfo");

  17:                     fault.Name = "ServiceExceptionInfoFault";

  18:                     fault.DetailType = typeof(ServiceExceptionInfo);

  19:                     op.Faults.Add(fault);

  20:                 }

  21:             }

  22:         }

  23:     }

  24: }

[WCF]缺少一行代码引发的血案的更多相关文章

  1. Myeclipse版本引发的css样式问题:头部自动生成一行代码导致样式引入不成功

    在运行新的项目之后,发现样式全部没了 利用开发者模式查看原因:是因为css样式文件的顶部自动生成了一行代码导致的 生成的代码:[genuitec-file-id="wc2-7"], ...

  2. 转:一个Sqrt函数引发的血案

    转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html 源码下载地址:http://diducoder.com/sotr ...

  3. 一个Sqrt函数引发的血案(转)

    作者: 码农1946  来源: 博客园  发布时间: 2013-10-09 11:37  阅读: 4556 次  推荐: 41   原文链接   [收藏]   好吧,我承认我标题党了,不过既然你来了, ...

  4. 一个无锁消息队列引发的血案(六)——RingQueue(中) 休眠的艺术 [续]

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  5. 一个无锁消息队列引发的血案(五)——RingQueue(中) 休眠的艺术

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  6. 一个无锁消息队列引发的血案(四)——月:RingQueue(上) 自旋锁

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  7. 一个无锁消息队列引发的血案(三)——地:q3.h 与 RingBuffer

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  8. 【转载】一个Sqrt函数引发的血案

    转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/sotry-about-sqrt.html 源码下载地址:http://diducoder ...

  9. 一个Sqrt函数引发的血案

    源码下载地址:http://diducoder.com/sotry-about-sqrt.html 好吧,我承认我标题党了,不过既然你来了,就认真看下去吧,保证你有收获. 我们平时经常会有一些数据运算 ...

随机推荐

  1. 探究javascript对象和数组的异同,及函数变量缓存技巧

    javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...

  2. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

  3. 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)

    前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...

  4. Code Review 程序员的寄望与哀伤

    一个程序员,他写完了代码,在测试环境通过了测试,然后他把它发布到了线上生产环境,但很快就发现在生产环境上出了问题,有潜在的 bug. 事后分析,是生产环境的一些微妙差异,使得这种 bug 场景在线下测 ...

  5. 简析服务端通过GT导入SHP至PG的方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中需要在浏览器端直接上传SHP后服务端进行数据的自动入PG ...

  6. web服务器集群

    概述 集群和分布式都是从集中式进化而来的.分布式和集群会相互合作的,同时的集群和分布式.在这里重点说说集群 集群是什么? 集群能提高单位时间内处理的任务数量,提升服务器性能 有多台服务器去处理任务,但 ...

  7. 5.2 Array类型的方法汇总

    所有对象都具有toString(),toLocaleString(),valueOf()方法. 1.数组转化为字符串 toString(),toLocaleString() ,数组调用这些方法,则返回 ...

  8. 嵌入式&iOS:回调函数(C)与block(OC)回调对比

    学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...

  9. Android 微信第三方登录(个人笔记)

    今天在写微信登录,花了半天时间搞定.然后写下自己的笔记,希望帮助更多的人...欢迎各位指教. 微信授权登录,官方说的不是很清楚.所以导致有一部分的坑. 微信注册应用平台的应用签名,下载 微信签名生成工 ...

  10. iOS开发--ChildViewController实现订单页的切换

    先不说废话, 上效果图, 代码量也不大, 也不上传github骗星星了, 你们复制粘贴下代码, 就可以轻而易举的弄出一个小demo. 这个代码的实现并不复杂, 甚至于说非常简单, 就是逻辑有点小绕, ...