[WCF]缺少一行代码引发的血案
这是今天作项目支持的发现的一个关于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]缺少一行代码引发的血案的更多相关文章
- Myeclipse版本引发的css样式问题:头部自动生成一行代码导致样式引入不成功
在运行新的项目之后,发现样式全部没了 利用开发者模式查看原因:是因为css样式文件的顶部自动生成了一行代码导致的 生成的代码:[genuitec-file-id="wc2-7"], ...
- 转:一个Sqrt函数引发的血案
转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html 源码下载地址:http://diducoder.com/sotr ...
- 一个Sqrt函数引发的血案(转)
作者: 码农1946 来源: 博客园 发布时间: 2013-10-09 11:37 阅读: 4556 次 推荐: 41 原文链接 [收藏] 好吧,我承认我标题党了,不过既然你来了, ...
- 一个无锁消息队列引发的血案(六)——RingQueue(中) 休眠的艺术 [续]
目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...
- 一个无锁消息队列引发的血案(五)——RingQueue(中) 休眠的艺术
目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...
- 一个无锁消息队列引发的血案(四)——月:RingQueue(上) 自旋锁
目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...
- 一个无锁消息队列引发的血案(三)——地:q3.h 与 RingBuffer
目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...
- 【转载】一个Sqrt函数引发的血案
转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/sotry-about-sqrt.html 源码下载地址:http://diducoder ...
- 一个Sqrt函数引发的血案
源码下载地址:http://diducoder.com/sotry-about-sqrt.html 好吧,我承认我标题党了,不过既然你来了,就认真看下去吧,保证你有收获. 我们平时经常会有一些数据运算 ...
随机推荐
- 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM
刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下 ...
- nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)
本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...
- Anders Hejlsberg 技术理想架构开发传奇
Anders Hejlsberg(安德斯-海森博格) 坐在自己的办公室,双眼直直的盯着前方.他要做一个决定,决定自己未来的命运和理想.这是1996年一个普通的下午,几个小时前,他刚与比尔-盖茨结束了 ...
- 初学seaJs模块化开发,利用grunt打包,减少http请求
原文地址:初学seaJs模块化开发,利用grunt打包,减少http请求 未压缩合并的演示地址:demo2 学习seaJs的模块化开发,适合对seajs基础有所了解的同学看,目录结构 js — —di ...
- Angular2 Hello World 之 RC6
angular2还没有发布正式版,确实有点不靠谱,变化太频繁,之前写的demo直接将js升级到最新版之后发现就不能用了……所以现在在写一篇demo——基于RC6.参考:http://web3.code ...
- 直播推流端弱网优化策略 | 直播 SDK 性能优化实践
弱网优化的场景 网络直播行业经过一年多的快速发展,衍生出了各种各样的玩法.最早的网络直播是主播坐在 PC 前,安装好专业的直播设备(如摄像头和麦克风),然后才能开始直播.后来随着手机性能的提升和直播技 ...
- 我的MYSQL学习心得(十一) 视图
我的MYSQL学习心得(十一) 视图 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
- 分布式服务注册和发现consul 简要介绍
Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其他分布式服务注册与发现的方案,Consul的方案更"一站式",内置了服务注册与发现框 架 ...
- 一不小心写了个WEB服务器
开场 Web服务器是啥玩意? 是那个托管了我的网站的机器么? No,虽然那个也是服务器,但是我们今天要说的Web服务器主要是指像IIS这样一类的,用于处理request并返回response的工具,没 ...
- Dubbo学习小记
前言 周一入职的新公司,到了公司第一件事自然是要熟悉新公司使用的各种技术,搭建本地的环境. 熟悉新公司技术的过程中,首先就是Maven,这个前面已经写过文章了,然后就是Dubbo----公司的服务都是 ...