`
varsoft
  • 浏览: 2521817 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创]WCF技术剖析之二十一:WCF基本异常处理模式[下篇]

阅读更多

从FaultContractAttribute的定义我们可以看出,该特性可以在同一个目标对象上面多次应用(AllowMultiple = true)。这也很好理解:对于同一个服务操作,可能具有不同的异常场景,在不同的情况下,需要抛出不同的异常。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
<!--CRLF-->
   2: public sealed class FaultContractAttribute : Attribute
<!--CRLF-->
   3: {    
<!--CRLF-->
   4:     //省略成员
<!--CRLF-->
   5: }
<!--CRLF-->

但是,如果你在同一个操作方法上面应用了多了FaultContractAttribute特性的时候,需要遵循一系列的规则,我们现在就来逐条介绍它们。

一、多次声明相同的错误明细类型

比如在下面的代码中,对于操作Divide,通过FaultContractAttribute特性对同一个错误明细类型CalculationError进行了两次设置。

   1: using System.ServiceModel;
<!--CRLF-->
   2: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   3: {    
<!--CRLF-->
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
<!--CRLF-->
   5:     public interface ICalculator
<!--CRLF-->
   6:     {
<!--CRLF-->
   7:         [OperationContract]
<!--CRLF-->
   8:         [FaultContract(typeof(CalculationError))]
<!--CRLF-->
   9:         [FaultContract(typeof(CalculationError))]
<!--CRLF-->
  10:         int Divide(int x, int y);
<!--CRLF-->
  11:     }   
<!--CRLF-->
  12: }
<!--CRLF-->

WCF服务端框架在初始化ServiceHost,并创建服务表述的时候(关于服务描述,以及在服务寄宿过程中对服务描述的创建,《WCF技术剖析(卷1)》的第7章有详细的介绍),会抛出如图1所示的InvalidOperationException异常。

clip_image002

图1 多次声明相同的错误明细类型导致的异常

但是,如果你在应用FaultContractAttribute特性指定相同错误明细类型的同时,指定不同的Name或者Namespace,这是允许的。比如下面的代码中,在两个FaultContractAttribute特性中,同样是指定的相同的错误明细类型CalculationError,由于我们为之指定了不同的Name,在寄宿服务的时候将不会有上述异常的发生。

   1: using System.ServiceModel;
<!--CRLF-->
   2: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   3: {    
<!--CRLF-->
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
<!--CRLF-->
   5:     public interface ICalculator
<!--CRLF-->
   6:     {
<!--CRLF-->
   7:         [OperationContract]
<!--CRLF-->
   8:         [FaultContract(typeof(CalculationError), Name = "CalculationError")]
<!--CRLF-->
   9:         [FaultContract(typeof(CalculationError), Name = "CalculationException")]
<!--CRLF-->
  10:         int Divide(int x, int y);
<!--CRLF-->
  11:     }   
<!--CRLF-->
  12: }
<!--CRLF-->

二、多次声明不同的具有相同有效名称错误明细类型

多次声明的错误类型的类型虽然不同,但是如果我们为其指定相同的Name和Namespace我们可以将Name和Namespace的组合称为有效名称QN:Qualified Name),这依然是不允许的。比如下面的代码中,通过FaultContractAttribute特性为Divide操作指定了两个不同的错误明细类型(CalculationError和CalculationException),但是设置的名称却是相同的(CalculationError)。

   1: using System.ServiceModel;
<!--CRLF-->
   2: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   3: {    
<!--CRLF-->
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
<!--CRLF-->
   5:     public interface ICalculator
<!--CRLF-->
   6:     {
<!--CRLF-->
   7:         [OperationContract]
<!--CRLF-->
   8:         [FaultContract(typeof(CalculationError), 
<!--CRLF-->
   9: Name = "CalculationError", Namespace = "http://www.artech.com/")]
<!--CRLF-->
  10:         [FaultContract(typeof(CalculationException), 
<!--CRLF-->
  11: Name = "CalculationError", Namespace = "http://www.artech.com/")]
<!--CRLF-->
  12:         int Divide(int x, int y);
<!--CRLF-->
  13:     }   
<!--CRLF-->
  14: }
<!--CRLF-->

对于这种情况,在服务寄宿的时候,依然会和上面一样抛出一个InvalidOperationExcepiton异常,如图2所示:

clip_image004

图2 多次申明具有相同有效名称导致的异常

三、多次声明不同的具有相同数据契约有效名称的错误明细类型

还有另一种情况:虽然是多次申明的是不同的错误明细类型,但是通过DataContractAttribute特性定义它们的时候,指定了相同的名称和命名空间。如果我们将它们通过FaultContractAttribute特性应用到同一个操作上面,又会出现怎样的问题了。比如,在下面的代码中,我们定义了两个不同错误明细类型(CalculationError和CalculationFault),它们具有相同的数据契约名称(CalculationError)和命名空间(http://www.artech.com/)。

   1: using System;
<!--CRLF-->
   2: using System.Runtime.Serialization;
<!--CRLF-->
   3: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   4: {
<!--CRLF-->
   5:     [DataContractAttribute(Namespace="http://www.artech.com/")]
<!--CRLF-->
   6:     public class CalculationError
<!--CRLF-->
   7:     {       
<!--CRLF-->
   8:         [DataMember]
<!--CRLF-->
   9:         public string Operation
<!--CRLF-->
  10:         { get; set; }
<!--CRLF-->
  11:         [DataMember]
<!--CRLF-->
  12:         public string Message
<!--CRLF-->
  13:         { get; set; }
<!--CRLF-->
  14:     }
<!--CRLF-->
  15: 
<!--CRLF-->
  16: [DataContractAttribute(Namespace = "http://www.artech.com/", Name = "CalculationError")]
<!--CRLF-->
  17:     public class CalculationFault
<!--CRLF-->
  18:     {
<!--CRLF-->
  19:         [DataMember]
<!--CRLF-->
  20:         public string Fault
<!--CRLF-->
  21:         { get; set; }
<!--CRLF-->
  22:     }
<!--CRLF-->
  23: }
<!--CRLF-->

如果我们通过下面的方式通过FaultContractAttribute特性将这两个类型应用到同一个服务操作上面,服务寄宿不会出什么问题,客户端的方法调用也能正常运行。

   1: using System.ServiceModel;
<!--CRLF-->
   2: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   3: {    
<!--CRLF-->
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
<!--CRLF-->
   5:     public interface ICalculator
<!--CRLF-->
   6:     {
<!--CRLF-->
   7:         [OperationContract]
<!--CRLF-->
   8:         [FaultContract(typeof(CalculationError))]
<!--CRLF-->
   9:         [FaultContract(typeof(CalculationFault))]
<!--CRLF-->
  10:         int Divide(int x, int y);
<!--CRLF-->
  11:     }   
<!--CRLF-->
  12: }
<!--CRLF-->

但是,当我们试图通过HTTP-GET或者标准的MEX终结点获取以WSDL表示的服务元数据(Metadata)的时候就会出现问题。至于为什么会导致这样的问题,你大体可以这样来理解:当WCF为某个操作的错误描述(Fault Description)的时候,会创建一个字典来存储通过FaultContractAttribute特性指定的基于错误明细类型的数据契约。对于这个字典来说,它的Key为数据契约的有效名称(QN:Qualified Name),即名称和命名空间组合。由于CalculationError和CalculationFault具有相同的名称和命名空间,这无疑会造成Key的冲突。

由于数据契约是使对数据结构的一种描述,如果两个数据契约时等效的,不管其具体的托管类型是什么,WCF在遇到上述情况的时候,会自动识别并忽略其中一个,从而保证元数据能够正确产生。比如说,如果我们将CalculationFault进行如下的改写,服务的元数据就能够被正常地获得了。

   1: [DataContractAttribute(Namespace = "http://www.artech.com/", Name = "CalculationError")]
<!--CRLF-->
   2: public class CalculationFault
<!--CRLF-->
   3: {
<!--CRLF-->
   4:     [DataMember(Name = "Operation")]
<!--CRLF-->
   5:     public string OperationName
<!--CRLF-->
   6:     { get; set; }
<!--CRLF-->
   7:     [DataMember(Name = "Message")]
<!--CRLF-->
   8:     public string Fault
<!--CRLF-->
   9:     { get; set; }
<!--CRLF-->
  10: }
<!--CRLF-->

四、通过XmlSerializer对错误明细对象进行序列化

对于任何分别是框架来说,序列化和反序列化都是其功能体系中重要的一环。WCF通过一个重要的对象实现对托管对象的序列化和反序列化:序列化器(Serializer)。具体来说,所有序列化和反序列化的功能又最终落实到两个具体的序列化器上:DataContractSerializer和XmlSerializer。关于这两种序列化器,在《WCF技术剖析(卷1)》第5章中已经有过深入的探讨,在这里就需要在画蛇添足了。

WCF采用的默认序列化器是DataContractSerializer,但是有的时候,我们需要显示地控制某个服务或者服务的某个操作的序列化行为,通过XmlSerializer来序列化和反序列化操作的参数对象和返回值。举个例子,一个服务的绝大部分操作的参数类型都是通过数据契约的方式定义,但是对于个别的操作参数类型依然沿用的是传统XML的定义方式。在这种情况下,我们希望的是专门对这几个操作进行定制,让它们采用XmlSerializer作为它们的序列化器。

我们可以通过对自定义特性System.ServiceModel.XmlSerializerFormatAttribute的应用帮助我们是相上面的功能。从先面对XmlSerializerFormatAttribute的定义我们可以看出:应用特性的目标元素的类型包括接口、类和方法。也就是说,XmlSerializerFormatAttribute既可以应用于服务契约接口上,也可以应用于服务类型上,甚至可以应用于服务接口和服务类型的方法上。

   1: [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
<!--CRLF-->
   2: public sealed class XmlSerializerFormatAttribute : Attribute
<!--CRLF-->
   3: {   
<!--CRLF-->
   4:     public XmlSerializerFormatAttribute();  
<!--CRLF-->
   5:     public OperationFormatStyle Style { get; set; }
<!--CRLF-->
   6:     public bool SupportFaults { get; set; }
<!--CRLF-->
   7:     public OperationFormatUse Use { get; set; }
<!--CRLF-->
   8: }
<!--CRLF-->

在默认的情况下,XmlSerializerFormatAttribute特性仅仅控制操作的参数和返回值的序列化行为,而不能控制错误明细对象的序列化行为。也就是说,基于在某个操作方法上应用了XmlSerializerFormatAttribute特性,WCF会采用XmlSerializer作为所有参数和返回值的序列化器,对于出现异常指定的错误明细对象,依然采用默认的DataContractSerializer进行序列化和反序列化。我们可以通过SupportFaults属性来显式地选择XmlSerializer作为错误明细对象的序列化器。在下面的代码中,我们将XmlSerializerFormatAttribute特性应用在服务契约的Divide操作上面,并将SupportFaults属性设为true。

   1: using System.ServiceModel;
<!--CRLF-->
   2: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   3: {    
<!--CRLF-->
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
<!--CRLF-->
   5:     public interface ICalculator
<!--CRLF-->
   6:     {
<!--CRLF-->
   7:         [OperationContract]
<!--CRLF-->
   8:         [FaultContract(typeof(CalculationError), Name = "CalculationError")]
<!--CRLF-->
   9:         [XmlSerializerFormat(SupportFaults = true)]
<!--CRLF-->
  10:         int Divide(int x, int y);
<!--CRLF-->
  11:     }
<!--CRLF-->
  12: }
<!--CRLF-->

那么对于Divide操作,WCF将会采用XmlSerializer同时作为参数、返回值和错误明细对象的序列化器。比如在这个时候,我们采用下面的形式对CalculationError进行重新定义:

   1: using System;
<!--CRLF-->
   2: using System.Runtime.Serialization;
<!--CRLF-->
   3: using System.Xml;
<!--CRLF-->
   4: using System.Xml.Serialization;
<!--CRLF-->
   5: namespace Artech.WcfServices.Contracts
<!--CRLF-->
   6: {
<!--CRLF-->
   7:     [Serializable]
<!--CRLF-->
   8:     public class CalculationError
<!--CRLF-->
   9:     {       
<!--CRLF-->
  10:         [XmlAttributeAttribute("op")]
<!--CRLF-->
  11:         public string Operation
<!--CRLF-->
  12:         { get; set; }
<!--CRLF-->
  13:         [XmlElement("Error")]
<!--CRLF-->
  14:         public string Message
<!--CRLF-->
  15:         { get; set; }
<!--CRLF-->
  16:     }
<!--CRLF-->
  17: }
<!--CRLF-->

在被零除而抛出异常的情况下,WCF将会生成如下一个Fault SOAP,其中s:Body>/<s:Fault>/ <s:Detail>节点中的XML为CalculationError对象序列化所的。

   1: <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<!--CRLF-->
   2:   <s:Header>
<!--CRLF-->
   3:     <a:Action s:mustUnderstand="1">http://www.artech.com/ICalculator/DivideCalculationError</a:Action>
<!--CRLF-->
   4:     <a:RelatesTo>urn:uuid:7b01995b-9f81-4a08-9fa2-c5ef8c7cacc</a:RelatesTo>
<!--CRLF-->
   5:   </s:Header>
<!--CRLF-->
border-style: none; margin: 0em; padding: 0px; overflow: visible; text-align: left; line-heig

  


  
分享到:
评论

相关推荐

    WCF基本异常处理模式[上篇].doc

    【WCF基本异常处理模式[上篇]]\n\nWindows Communication Foundation(WCF)是微软提供的一个用于构建分布式应用程序的服务框架。WCF以其强大的功能和灵活性,成为了.NET平台上的首选技术之一。在WCF中,异常处理是...

    WCF全面解析(套装上下册) 高清pdf 带书签 part2(共4)

    《WCF全面解析(套装上下册)》的下册主要涉及一些所谓的“高级”话题,主要包括如何在分布式环境中处理异常(第1章);元数据的导入与导出、发布与获取如何实现(第2章);如何利用WCF对事务的支持将分布式事务引入...

    WCF技术剖析(word文档)

    这篇文档是关于.NET下的WCF技术,适合想要在.NET环境下进行分布式开发的学习者。 **WCF基础架构** 在WCF框架中,服务端和客户端的交互涉及多个组件,主要包括: 1. **请求接收与回复发送**:服务端通过传输层监听...

    WCF全面解析(下册)

    《WCF全面解析(套装上下册)》的下册主要涉及一些所谓的“高级”话题,主要包括如何在分布式环境中处理异常(第1章);元数据的导入与导出、发布与获取如何实现(第2章);如何利用WCF对事务的支持将分布式事务引入...

    WCF全面解析(源码)

    《WCF全面解析(套装上下册)》的下册主要涉及一些所谓的“高级”话题,主要包括如何在分布式环境中处理异常(第1章);元数据的导入与导出、发布与获取如何实现(第2章);如何利用WCF对事务的支持将分布式事务引入...

    WCF全面解析(上册)

    《WCF全面解析(套装上下册)》的下册主要涉及一些所谓的“高级”话题,主要包括如何在分布式环境中处理异常(第1章);元数据的导入与导出、发布与获取如何实现(第2章);如何利用WCF对事务的支持将分布式事务引入...

    Rest WCF资源文件

    RESTful WCF服务是通过WCF技术实现的一种遵循REST原则的Web服务。它利用HTTP方法(如GET、POST、PUT、DELETE)来操作资源,以URI(Uniform Resource Identifier)作为资源的唯一标识。这种服务易于理解和实现,且与...

    WCF会话行为

    本篇将深入探讨WCF的三种会话方式,以及它们在不同实例模式下的应用,并分析实例、会话和通信协议的组合效果。 ### 1. 会话类型 WCF中的会话主要分为以下三种: 1. **无会话(NoSession)**:这是默认模式,服务...

    WCF 分布式开发转载

    **分布式开发中的WCF技术详解** Windows Communication Foundation (WCF) 是微软.NET Framework的一部分,它为构建可伸缩、安全和可靠的分布式应用程序提供了一个统一的编程模型。在本篇文章中,我们将深入探讨WCF...

    由一个需求聊聊WCF文章示例

    我们将通过分析示例程序SilverlightWcfExample和相关文档,来揭示WCF的核心概念和技术。 首先,WCF是.NET Framework中的一个组件,用于构建服务导向的应用程序。它集成了多种通信协议,如HTTP、TCP、SMTP等,使得...

    WCF RIA 服务 (二十五)-- Silverlight 客户端 6 源代码

    在本篇中,我们将深入探讨WCF RIA(Windows Communication Foundation Rich Internet Application)服务与Silverlight客户端6的源代码交互。WCF RIA服务是一种强大的技术,它允许开发者构建跨域分布式应用程序,尤其...

    传说中的WCF(10)的例子代码

    在IT行业中,Windows Communication Foundation(WCF)是微软.NET Framework框架下的一种面向服务的通信架构。它为构建可互操作的、安全的、可靠的服务提供了全面的平台。本篇文章将聚焦于“传说中的WCF(10)的例子...

    「云安全」信息安全_数据安全_Abusing WCF Endpoints for RCE and Priv - 数据安全.

    10. **数据分析**:利用数据分析技术,通过行为分析识别潜在的威胁模式。 综上所述,了解并防范WCF服务的潜在风险是保障云安全的关键步骤。通过合理的配置、权限管理、安全实践和持续监控,可以大大降低数据被攻击...

    分享WCF文件传输实现方法---WCFFileTransfer

    总之,文章介绍了通过WCF实现文件传输的基本方法,并通过接口分离和通道创建客户端代理等高级技术点,提供了深入理解和实践WCF文件传输的途径。同时,也展示了WCF在通信协议、绑定设置、接口定义和消息传输方面的...

    WCF 消息压缩 自定义验证(X509证书指纹验证)

    本篇文章将深入探讨如何在WCF中实现消息压缩以及自定义的X509证书指纹验证,这对于确保数据安全传输和优化网络资源使用至关重要。 首先,我们来了解**WCF消息压缩**。为了减少网络传输的数据量,WCF支持GZip和...

    Windows平台下分布式技术比较综述.pdf

    综上所述,这篇文档深入分析了Windows平台下分布式技术的现况,并提供了WCF技术与其他分布式技术的详细比较。文档强调了WCF在统一性、互操作性、安全性、兼容性以及开发简化方面的优势,并对WCF之前的分布式技术如...

    KTV点歌系统

    本篇将深入探讨该系统在开发过程中所采用的关键技术——C#、WCF(Windows Communication Foundation)以及VLC(VideoLAN Client),并分析它们如何协同工作以提供流畅的点歌体验。 首先,C#是一种由微软公司推出的...

    C# yield在WCF中的错误使用(二)

    这篇文章将深入探讨 `yield` 在WCF中的常见错误用法。 ### 一、`yield` 的基本概念 `yield` 是C#语言中用于创建迭代器的一个关键字,它可以使得方法能够按需生成序列的元素,而无需一次性构建整个序列。通常,`...

Global site tag (gtag.js) - Google Analytics