谈谈WCF中的Data Contract (1):Data Contract Overview

简介:
Contract in SOContract是对操作和数据的抽象  

在我们看来,Service Orientation提供了一种对业务、功能进行分解的方式。针对SO,我们把一个具体的业务流程或者一个复杂的功能分解成一个个独立完成某项任务的子单元,这些子单元通过一个个Service来承载。对于Service本身来讲,他们应该是自治的,独自完成自己的功能、不依赖于其他的Service。但是Service的价值体现在它被潜在的消费者使用的程度。这实际上包含两方面的内容,作为Service本身,它如何将自己暴露出来,供一切可能的潜在用户调用,这些潜在用户不仅仅指那些不同的Client,也包含其他的ServiceService Orientation其中一个特征就是“Service should be composite,鼓励将一个个相关细粒度的Service组合成一个大的Service。这样有利于较大限度的实现重用,而重用往往意味着更小的投入、更佳的可维护性。而另一方面就是这些消费者通过怎样的方式来调用它所需要的Service

这实际上体现了两者相互交互的问题。在一个分布式的环境中要实现两者的交互,有两个必须要解决的问题:如何保证Service的使用者对Service的调用能够被Service端理解,以及对Service的调用如何抵达Service Side。后者实质上是关于communication的问题,我们现在不去谈它。第一个问题就是Contract需要解决的问题。

我们知道SOA一个主要的目标就是促进不同技术平台的互操作,要真正实现这样一个宏伟的目标是一件极不容易的事情,需要不同的厂商和标准组织相互协作,制定一个大家一致遵循的标准。这样一个标准就是WS-* 。我们很清楚,无论个个厂商各自的标准怎样千差万别,但是有个标准是他们必须要遵循的,那就是Internet的标准,如果哪家公司拒绝Internet,那肯定要被淘汰的。而对于Internet,基于Http的网络协议和基于XML的数据表达已经成为了事实上的标准。对于SOA来说,XML不仅仅用于表示Service调用携带的数据(参数和返回值),更用于表示这个调用本身,以及满足各种要求的控制信息, 比如基于SecuritySessionReliable MessagingTransaction等等的控制信息。WS-*就是一个基于XML的标准。而对于SOA中的Contract所要做的就是寻求一种厂商中立的方式来表示Service的接口、和用于交互的数据结构。前者就是Service Contract、后者就是Data Contract

SOA中的一个Service由一组相关的Operation来构成。Service Contract用于表示构成该Service所有OperationInterface(而不是Implementation)。说得更加具体点,大家都知道ConsumerService之间的交互都是通过Message的形式来实现的,一次交互就是一次Message Exchange。在不同的场景,我们以不通过Pattern来进程Message Exchange,比如我们通常使用Request-Response的方式来向Service发送Request进而得到返回结果,我们也可以以Request-Forget的形式来异步地调用Service(不需要从Service获取Response),我们可以让一个Service在没有收到任何Request的情况下,以广播的形式向注册的Client发送通知,当然我们还有其他不同的消息交互的模式,我们把这些不同的信息交互方式称为MEPMessage Exchange Pattern)。也就是说,一个Operation最终通过被最终转换成了按照某种MEP进行的消息交互,而Service Contract旨在实现对这种MEP的描述,比如是否需要Request Message或者Response Message(如果仅仅有Response Message就是Notification的方式;如果仅仅具有Request Message,那就是我们上面谈到的Request-Forget的模式),和Message本身具有的格式。

上面我们说了Service Contract是以一种厂商中立的形式描述体现为某种模式的消极交互、构成整个Service的所有Operation。而我们也说了ConsumerService的交互本质上看就是按照某种Pattern体现的一次Message Exchange,好像具有了Service Contract的描述就可以了。但是实际上,单单有了Service ContractService的描述还不够,因为Service Contract本身缺乏对携带于Message,用于信息传递的数据类型的描述,而这是Data Contract需要解决的问题。我们知道不同的技术平台对数据类型的表示是不一样的,可能某一种技术平台使用16bit来表述一个浮点数,另一种则使用32bit。所以要想实现不同技术平台的互操作,将不同技术平台同一类型的数据以一种厂商中立的形式来描述是必须的。

概括的说,SOA中的Service ContractData Contract就是一种厂商中立的数据呈现方式对Service InterfaceData Type的。而Service的调用都是通过SOAP Message来实现,SOAP是基于XML,而对于XML结构的定义,我们很自然地想到XSD,我们可简单地将SOA中的Contract看成是一个XSD 

Contract in WCF 

上面我们实际上是在一个厂商中立的前提下探讨Contract,这里的Contract和具体的平台和技术无关。接下来我们要谈的是基于技术的话题:讨论一下WCF下的Contract。简单地说,WCF中的Contract主要的功能就是如何将一个基于.NETCLR TypeInterface或者Class,转化成一个我们上面提到的Neutral Contract。比如,如果我们在一个Interface和它的成员上分别运用Service Contract AttributeOperation Contract,当我们Host实现了该InterfaceService的时候,WCF就能将在一个.NET-specificCLR Type暴露成一个Neutral Service Contract。同理对于一个,我们通过在一个Class和它的成员上分别添加DataContractAttributeDataMemberAttribute,就可以就该CLR Type转变成Neutral Data Contract

比如我们一个运用了DataContractAttributeDataMemberAttributeOrder class

namespace Artech.DataContractVersioning.Service
{
    [DataContract(Namespace="http://artech.datacontractversioning")]
    public class Order 
    {
        [DataMember(Order = 0)]
        public Guid OrderID
        {get;set;}

        [DataMember(Order = 1)]
        public DateTime OrderDate
        getset; }

        [DataMember(Order = 2)]
        public Guid SupplierID
        getset; }
    }

}

就可以转变成另一种厂商中立的、以XSD表示的Neutral Data Contract

<? xml version="1.0" encoding="utf-8"  ?>
< xs:schema  elementFormDefault ="qualified"  targetNamespace ="http://artech.datacontractversioning"
    xmlns:xs
="http://www.w3.org/2001/XMLSchema"  xmlns:tns ="http://artech.datacontractversioning"
    xmlns:ser
="http://schemas.microsoft.com/2003/10/Serialization/" >
< xs:import  schemaLocation ="http://artech/Artech.DataContractVersioning/OrderManagerService.svc?xsd=xsd1"
    namespace
="http://schemas.microsoft.com/2003/10/Serialization/"   />
< xs:complexType  name ="Order" >
     < xs:sequence >
         < xs:element  minOccurs ="0"  name ="OrderID"  type ="ser:guid"   />
         < xs:element  minOccurs ="0"  name ="OrderDate"  type ="xs:dateTime"   />
         < xs:element  minOccurs ="0"  name ="SupplierID"  type ="ser:guid"   />
     </ xs:sequence >
</ xs:complexType >< xs:element  name ="Order"  nillable ="true"  type ="tns:Order" /></ xs:schema >

Client需要调用该Order typeService的时候,在本地需要一个Data Type能够匹配上面的以XSD体现的Data Contract。一般地,我们可以在VS中通过Add Service Reference的方式或者通过一些Tools,比如XSDUtilSvcUtil来生成这样的Class。比如我们通过Add Service Reference方式,就可以生成下面一个对应的Order class:

 

[System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="Order", Namespace="http://artech.datacontractversioning")]
    [System.SerializableAttribute()]
     public partial  class Order :  object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged  {
        
        [System.NonSerializedAttribute()]
        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private System.Guid OrderIDField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private System.DateTime OrderDateField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private System.Guid SupplierIDField;
        
        [global::System.ComponentModel.BrowsableAttribute(false)]
        public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
            get {
                return this.extensionDataField;
            }

            set {
                this.extensionDataField = value;
            }

        }

        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public System.Guid OrderID {
            get {
                return this.OrderIDField;
            }

            set {
                if ((this.OrderIDField.Equals(value) != true)) {
                    this.OrderIDField = value;
                    this.RaisePropertyChanged("OrderID");
                }

            }

        }

        
        [System.Runtime.Serialization.DataMemberAttribute(Order=1)]
        public System.DateTime OrderDate {
            get {
                return this.OrderDateField;
            }

            set {
                if ((this.OrderDateField.Equals(value) != true)) {
                    this.OrderDateField = value;
                    this.RaisePropertyChanged("OrderDate");
                }

            }

        }

        
        [System.Runtime.Serialization.DataMemberAttribute(Order=2)]
        public System.Guid SupplierID {
            get {
                return this.SupplierIDField;
            }

            set {
                if ((this.SupplierIDField.Equals(value) != true)) {
                    this.SupplierIDField = value;
                    this.RaisePropertyChanged("SupplierID");
                }

            }

        }

        
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        
        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(thisnew System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }

        }

    }

通过上面这样一个在Client自动生成的Order class,你就可以创建Order对象来调用相应的Service了。这种自动生成代码的方式确实很省事,而且当Service端的Data Contract改变的时候,你只需要Update Service Reference就可以重新生成并覆盖现有的代码。但是,就我个人来说,我不要喜欢使用这样的方式,如果对Service暴露出来的数据结构很熟悉的话,我宁愿自己编写这样的class。特别地,对于WCF-WCFClientService都是WCF),如果可能的话,让定义ContractAssemblyServicecontract共享,我想是最直接的方式。

上面我们说所说的都是根据Service暴露出来的、以厂商中立方式体现的(比如XSDClient端生成或者自行创建与之相对的Data type。但是对于下面这样的场景,重建Data Type却不是一个好的选择:Client现在已经有一个Order class,而且很多的业务逻辑均依赖于这个class,现在需要调用一个现有的Order Processing ServiceOrder作某种处理,但是Service Order Type,说得更准确地,Service暴露出来的Order Data ContractClient现有的Order class不太一致,很显然在这种情况下,Client端部可能使用本地Order对象来调用该Service,因为Client提供的数据不符合该Data Contract,如果想上面讲到了重新生成或者创建一个新的Order class,就意味着其他依赖于现有Order class的业务逻辑均会受其影响。所以,在这里,我们需要WCF Data Contract提供给我们的另一种功能——适配功能:通过现有的CLR Type上添加或者改变DataContractAttribute 或者DataMemberAttribute的参数来使现有的CLR Type符合一个既定的Data Contract。究其本质,无论将CLR Type暴露成一个Neutral Contract也好,将CLR Type与既定的Neutral Contract进行适配也罢,这两种功能都是等效的。

接下来,我们就根据一个例子来讨论WCF Data Contract如何将一个现有的CLR Type与一个既定的Neutral Data Contract匹配。

Data Contract Mapping Mechanism 

通过上面的介绍,我们发现WCF Data Contract就如同一个适配器,弥合了 CLR TypeNeutral Contract的差异,很容易地实现了他们之间的匹配。接下来,我们就以一个实际的例子来介绍WCF DataContract的这种适配功能:通过DataContractAttribute的修饰,实现了将一个现有Data Type向一个既定的Neutral Data Contract进行适配,从而实现了对基于该Neutral Data ContractService 进行正常调用的目的。

我们就以上面提到的Order Class为例,Service端的Order class最终暴露成一个以XSD表示的Neutral Contract

Order class

namespace Artech.DataContractVersioning.Service
{
    [DataContract(Namespace="http://artech.datacontractversioning")]
    public class Order 
    {
        [DataMember(Order = 0)]
        public Guid OrderID
        {get;set;}

        [DataMember(Order = 1)]
        public DateTime OrderDate
        getset; }

        [DataMember(Order = 2)]
        public Guid SupplierID
        getset; }
    }

}

XSD

<? xml version="1.0" encoding="utf-8"  ?>
< xs:schema  elementFormDefault ="qualified"  targetNamespace ="http://artech.datacontractversioning"
    xmlns:xs
="http://www.w3.org/2001/XMLSchema"  xmlns:tns ="http://artech.datacontractversioning"
    xmlns:ser
="http://schemas.microsoft.com/2003/10/Serialization/" >
< xs:import  schemaLocation ="http://artech/Artech.DataContractVersioning/OrderManagerService.svc?xsd=xsd1"
    namespace
="http://schemas.microsoft.com/2003/10/Serialization/"   />
< xs:complexType  name ="Order" >
     < xs:sequence >
         < xs:element  minOccurs ="0"  name ="OrderID"  type ="ser:guid"   />
         < xs:element  minOccurs ="0"  name ="OrderDate"  type ="xs:dateTime"   />
         < xs:element  minOccurs ="0"  name ="SupplierID"  type ="ser:guid"   />
     </ xs:sequence >
</ xs:complexType >< xs:element  name ="Order"  nillable ="true"  type ="tns:Order" /></ xs:schema >

设我们在Client有一个与之结构相似的CustomOrder class

public  class CustomOrder
     {
        public Guid OrderNo
        getset; }

        public Guid SupplierNo
        getset; }

        public DateTime OrderDate
        getset; }
}

仔细分析CustomOrderServiceOrder以及XSD,我们发现两者除了结构一样之外,没有一处使相同的,具体体现在:         

  • Class Name 不一样。
  • NameSpace 不一样, Order XSD namespace http://artech.datacontractversioning ,而 CustomOrder 却没有显示指定一个 Namespace (这样 WCF 会为其指定一个默认的 namespace )。
  • 成员的名称不一样。
  • 成员的顺利不一样。

如果我们现在要使我们的CustomOrder满足现有的Order Data Contract,我们就需要消除这些不同之处,通过DataContractAttributeDataMemberAttribute,这样的问题根本就不是问题,下面就是我们重新定义的CustomOrder class

  [DataContract(Name="Order",Namespace="http://artech.datacontractversioning")]
     public  class CustomOrder
     {
        [DataMember(Order = 0, Name="OrderID")]
        public Guid OrderNo
        getset; }

        [DataMember(Order = 2, Name = "SupplierID")]
        public Guid SupplierNo
        getset; }

        [DataMember(Order = 1)]
        public DateTime OrderDate
        getset; }

        [DataMember(Order = 3)]
        public string ShippingAddress
        getset; }
}

通过在DataContractAttribute指定NameNamespace使Data ContractNamespace和既定的Contract相匹配,通过DataMemberAttributeNameOrder参数是成员的名称和次序与既定的Contract相匹配。

[原创]谈谈WCF中的Data Contract(1):Data Contract Overview
[原创]谈谈WCF中的Data Contract(2):WCF Data Contract对Generic的支持
[原创]谈谈WCF中的Data Contract(3):WCF Data Contract对Collection & Dictionary的支持
[原创]谈谈WCF中的Data Contract(4):WCF Data Contract Versioning


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
.NET
艾伟:[原创]谈谈WCF中的Data Contract(2):WCF Data Contract对Generic的支持
通过第一部分的介绍,我们可以体会到,WCF 的Data Contract在CLR Type和Neutral Contract之间搭建了一座桥梁,弥合了.NET世界和厂商中立世界的差异。通过WCF Data Contract我们将CLR Data Type暴露成一个厂商中立的数据结构的描述,同样通过WCF Data Contract我们将一个现有的CLR Data Type和既定的Neutral contract进行适配。
756 0
艾伟:[原创]谈谈WCF中的Data Contract(3):WCF Data Contract对Collection & Dictionary的支持
在本篇文章上一部分Order Processing的例子中,我们看到原本已Collection形式定义的DetailList属性(public IList DetailList),在Data Contract中却以Array的方式体现(public OrderDetail[] DetailList)。
890 0
艾伟:[原创]谈谈WCF中的Data Contract(4):WCF Data Contract Versioning
软件工程是一门独特的工程艺术,需要解决的是不断改变的需求变化。而对于WCF,对于SOA,由于涉及的是对多个系统之间的交互问题,如何有效地解决不断改变的需求所带来的问题就显得更为重要:Service端版本的变化能否保持现有Consumer的正常调用,Consumer端的改变不至于影响对Service 的正常调用。
903 0
|
XML 安全 数据格式
WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)
原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]大部分的系统都是以数据为中心的(Data Central),功能的实现表现在对相关数据的正确处理。
944 0
|
XML .NET 数据格式
wcf 基础教程 契约 Contract 数据契约DataContract序列化前身 XmlSerializer xml序列化
本来今天打算描述一下数据契约的序列化,毕竟只是单纯的说数据契约的作用也没有太大意义,但是我发现如果单纯的叙述wcf的序列胡DataSerializer 很困难,因为它采用的事xml序列化,所以今天打乱了我的计划,来介绍一下.Net中的xml序列化,毕竟我们在使用序列化器的时候,很多时候生成的都是xml。
793 0
|
XML 数据格式
wcf 基础教程 契约 Contract 控制xml输出 数据契约DataContract序列化前身 XmlSerializer xml序列化
在上一篇博客介绍了xml序列化的原则,wcf 基础教程 契约 Contract 数据契约DataContract序列化前身 XmlSerializer xml序列化, 今天我们沿着上次描述的继续前进,这次的内容可能会很少,但是应该说如果我们想更进一步的控制xml,那么还是很有必要的。
800 0
|
XML 开发框架 前端开发
wcf基础教程之 契约(合同)Contract
在前几篇博客中我有说到服务的寄宿,就是服务要运行起来必须采取的几种方式,相当于我们可以照葫芦画瓜的效果运行一个wcf服务,但是那只是实践,我们知其然更要知其所以然,所以从今天开始我们开始介绍wcf的三大部分:契约、绑定以及服务。
941 0
|
.NET 开发者 开发框架
Silverlight中服务通信方式的选择(WCF、Data Service、Ria Service)
转自 http://www.cnblogs.com/024hi/archive/2011/06/23/2088295.html WCF Service(WebService) Web Services是经实践考验证明的跨防火墙的通信方式,它很稳定且被广泛认可。
1079 0