通过第一部分的介绍,我们可以体会到,WCF 的Data Contract在CLR Type和Neutral Contract之间搭建了一座桥梁,弥合了.NET世界和厂商中立世界的差异。通过WCF Data Contract我们将CLR Data Type暴露成一个厂商中立的数据结构的描述,同样通过WCF Data Contract我们将一个现有的CLR Data Type和既定的Neutral contract进行适配。

在.NET中,基于Primary Type,比如Int32,String等等,他们具有一个简单的默认的序列化方式和结构,可以说他们不需要Data Contract。接下来我们主要讨论的是一些相对比较特殊的、完全基于.NET的Data Type,比如Generic、Collection,和Dictionary。首先,我们结合例子来谈谈基于Generic的Data Type的Data Contract。

假设我们需要创建一个用于处理一些单据(Bill)的Service,比如如Order Bill、Sales Bill等。一般的单据都有一个单据头(Header)和明细(Detail)列表,为此我们创建了一个Generic的Bill。并

namespace Artech.SpecialDataContract.Contract
{
    [DataContract]
    public class Bill<THeader, TDetail>
    {
        [DataMember]
        public THeader Header
        { get; set; }

        [DataMember]
        public IList<TDetail> DetailList
        { get; set; }
    }

    [DataContract]
    public class OrderHeader
    {
        [DataMember]
        public Guid OrderID
        { get; set; }

        [DataMember]
        public DateTime OrderDate
        { get; set; }
    }

    [DataContract]
    public class OrderDetail
    {
        [DataMember]
        public Guid ProductID
        { get; set; }

        [DataMember]
        public int Quantity
        { get; set; }
    }

}

为处理订单单据创建了机遇订单的Header和Detail。

对于一个Neutral Service Contract和Neutral Data Contract本身是不可能支持Generic的,也就是Neutral Contract只能是对一个具体的CLR Type的体现。所以在定义Service Contract的时候,对于那些包含Generic Type作为参数或者返回值得Operation,我们必须指定一个具体的Data Type。所以我们创建了如下一个IBillManager Service Contract:

namespace Artech.SpecialDataContract.Contract
{
    [ServiceContract]
    public interface IBillManager
    {
        [OperationContract]
        void Procss(Bill<OrderHeader, OrderDetail> orderBill);
    }
}

如何我们现在

Host基于这样一个Contract的Service,你猜我们作为参数的数据类型将会如何体现的。

通过WSDL,我们会发现该Service的Data Contract将会以下面一段XSD的方式来呈现:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract"
    xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">
  <xs:import schemaLocation="http://artech/Artech.SpecialDataContract/BillManagerService.svc?xsd=xsd1"
      namespace="http://schemas.microsoft.com/2003/10/Serialization/" />
  <xs:complexType name="BillOfOrderHeaderOrderDetailLZ9Dq20o">
    <xs:annotation>
      <xs:appinfo>
        <GenericType Name="BillOf{0}{1}{#}" Namespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract"
            xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
          <GenericParameter Name="OrderHeader" Namespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract"/>
          <GenericParameter Name="OrderDetail" Namespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract"/>
        </GenericType>
      </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
      <xs:element minOccurs="0" name="DetailList" nillable="true" type="tns:ArrayOfOrderDetail"/>
      <xs:element minOccurs="0" name="Header" nillable="true" type="tns:OrderHeader"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="BillOfOrderHeaderOrderDetailLZ9Dq20o" nillable="true" type="tns:BillOfOrderHeaderOrderDetailLZ9Dq20o"/>
  <xs:complexType name="ArrayOfOrderDetail">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="OrderDetail" nillable="true" type="tns:OrderDetail"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="ArrayOfOrderDetail" nillable="true" type="tns:ArrayOfOrderDetail"/>
  <xs:complexType name="OrderDetail">
    <xs:sequence>
      <xs:element minOccurs="0" name="ProductID" type="ser:guid"/>
      <xs:element minOccurs="0" name="Quantity" type="xs:int"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="OrderDetail" nillable="true" type="tns:OrderDetail"/>
  <xs:complexType name="OrderHeader">
    <xs:sequence>
      <xs:element minOccurs="0" name="OrderDate" type="xs:dateTime"/>
      <xs:element minOccurs="0" name="OrderID" type="ser:guid"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="OrderHeader" nillable="true" type="tns:OrderHeader"/>
</xs:schema>

对于不习惯看XSD的朋友,我们可以通过Add Service Reference的方式创建本地的Proxy file,借助生成的与之对应的Class来看看这个XSD最终呈现的结构:

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="BillOfOrderHeaderOrderDetailLZ9Dq20o", Namespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract")]
    [System.SerializableAttribute()]
    public partial class BillOfOrderHeaderOrderDetailLZ9Dq20o : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
        
        [System.NonSerializedAttribute()]
        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private Artech.SpecialDataContract.Client.BillManagerService.OrderDetail[] DetailListField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private Artech.SpecialDataContract.Client.BillManagerService.OrderHeader HeaderField;
        
        [global::System.ComponentModel.BrowsableAttribute(false)]
        public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
            get {
                return this.extensionDataField;
            }
            set {
                this.extensionDataField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public Artech.SpecialDataContract.Client.BillManagerService.OrderDetail[] DetailList {
            get {
                return this.DetailListField;
            }
            set {
                if ((object.ReferenceEquals(this.DetailListField, value) != true)) {
                    this.DetailListField = value;
                    this.RaisePropertyChanged("DetailList");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public Artech.SpecialDataContract.Client.BillManagerService.OrderHeader Header {
            get {
                return this.HeaderField;
            }
            set {
                if ((object.ReferenceEquals(this.HeaderField, value) != true)) {
                    this.HeaderField = value;
                    this.RaisePropertyChanged("Header");
                }
            }
        }
        
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        
        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="OrderHeader", Namespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract")]
    [System.SerializableAttribute()]
    public partial class OrderHeader : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
        
        [System.NonSerializedAttribute()]
        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private System.DateTime OrderDateField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private System.Guid OrderIDField;
        
        [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.DateTime OrderDate {
            get {
                return this.OrderDateField;
            }
            set {
                if ((this.OrderDateField.Equals(value) != true)) {
                    this.OrderDateField = value;
                    this.RaisePropertyChanged("OrderDate");
                }
            }
        }
        
        [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");
                }
            }
        }
        
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        
        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="OrderDetail", Namespace="http://schemas.datacontract.org/2004/07/Artech.SpecialDataContract.Contract")]
    [System.SerializableAttribute()]
    public partial class OrderDetail : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
        
        [System.NonSerializedAttribute()]
        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private System.Guid ProductIDField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        private int QuantityField;
        
        [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 ProductID {
            get {
                return this.ProductIDField;
            }
            set {
                if ((this.ProductIDField.Equals(value) != true)) {
                    this.ProductIDField = value;
                    this.RaisePropertyChanged("ProductID");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int Quantity {
            get {
                return this.QuantityField;
            }
            set {
                if ((this.QuantityField.Equals(value) != true)) {
                    this.QuantityField = value;
                    this.RaisePropertyChanged("Quantity");
                }
            }
        }
        
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        
        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }

为了使大家一眼就能了解整个结构,我对上面的代码进行简化:

namespace Artech.SpecialDataContract.Client.BillManagerService
{
    [DataContract]
    public class BillOfOrderHeaderOrderDetailLZ9Dq20o
    {
        [DataMember]
        public OrderHeader Header 
        { get; set; }

        [DataMember]
        public OrderDetail[] DetailList
        { get; set; }
    }

    [DataContract]
    public class OrderHeader
    {
        [DataMember]
        public Guid OrderID
        { get; set; }

        [DataMember]
        public DateTime OrderDate
        { get; set; }
    }

    [DataContract]
    public class OrderDetail
    {
        [DataMember]
        public Guid ProductID
        { get; set; }

        [DataMember]
        public int Quantity
        { get; set; }
    }

}

我们可以通过上面的code,注意到下面的细节:

·         Generic class Bill<THeader, TDetail>没有了,取而代之的是使用了具体OrderHeader和OrderDetial的新的非Generic class:BillOfOrderHeaderOrderDetailLZ9Dq20o。正如我们在上面所说,Neutral Contract根本就不知道Generic为何物。

·         新的Class name的名称很难看,它有下面几个部分组成:Bill(Generic Type Name)+ Of + OrderHeader(Generic Type的第一个类型参数对应的具体类型名称)+OrderDetail(Generic Type的第二个类型参数对应的具体类型名称)+lLZ9Dq20o(Generic Type参数类型Namespace的Hash Value)。

·         原本使用IList表示的DetailList变成了Array(public OrderDetail[] DetailList),这个将在和面的部分介绍。

我想你也不能容忍生成的如此冗长、甚至没有太大意义的Class name。我们有办法生成一个友好的名称。那就是显示指定Data Contract的Name:

[DataContract(Name="OrderBill")]
    public class Bill<THeader, TDetail>
    {
        [DataMember]
        public THeader Header
        { get; set; }

        [DataMember]
        public IList<TDetail> DetailList
        { get; set; }
}

现在对应的Data Contract Name将变成我们指定的名称。

public partial class OrderBill : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {}

但是上面的做法是不对的。原因很简单,如果我们在Service中添加一个处理Sales Bill的Operation(当然我们会添加两个额外的Header和Detail:SalesHeader和SalesDetail):

    [ServiceContract]
    public interface IBillManager
    {
        [OperationContract(Name=”ProcessOrderBill”)]
        void Procss(Bill<OrderHeader, OrderDetail> orderBill);

        [OperationContract(Name =”ProcessSalesBill”)]
        void Procss(Bill<SalesHeader, SalesDetail> salesBill);
    }

很显然,WCF需要为Order Bill和Sales Bill创建两个Data Contract,但是现在你却把他们的名称显示地限定到一个固定的名称,很显然这会造成命名的冲突。如果你通过Browser试图访问Service,你会得到如下的Error:

The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.

所以WCF需要为此提供这样的机制:基于不同的泛型类型参数生成不同Data Contract Name,这样才能解决命名冲突。我们可以稍微修改一下Data Contract 的定义就可以了:

    [DataContract(Name="Bill_{0}_{1}")]
    public class Bill<THeader, TDetail>
    {
        [DataMember]
        public THeader Header
        { get; set; }

        [DataMember]
        public IList<TDetail> DetailList
        { get; set; }
}

其中{0}和{1}分别代表第一个泛型类型参数和第二个泛型类型参数的名称,一次类推,你可以根据参数类型的个数设置{2}{3}…

这样我们生成的两个DataContract的名称为:Bill_OrderHeader_OrderDetailBill_SalesHeader_SalesDetail

但是这并没有根本解决问题,如果在我现在不同的Namespace中创建了两个OrderHeader和OrderDetail呢?这无疑在.NET中是合法的,但是对于DataContract有有可能出现命名冲突。

这也就是为什么WCF默认机制下会为Data Contract Name添加一个额外hash value的原因。其实你也可以以你自己的方式添加这个Hash value:

[DataContract(Name="Bill_{0}_{1}_{#}")]
    public class Bill<THeader, TDetail>
    {
        [DataMember]
        public THeader Header
        { get; set; }

        [DataMember]
        public IList<TDetail> DetailList
        { get; set; }
}

上面的

{#}就代表这样一个Hash Value, 我想你会想到现在生成的Data Contract Name象什么样子:Bill_OrderHeader_OrderDetail_LZ9Dq20o 和Bill_SalesHeader_SalesDetail_LZ9Dq20o

[原创]谈谈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

谈谈WCF中的Data Contract(2):WCF Data Contract对Generic的支持相关推荐

  1. wcf中如何Host多个WCF服务?

    如果用self-host的方式来加载服务的话,我们一般是用这样的代码:ServiceHost host1 = new ServiceHost(typeof(UserService)); 问题是,如果当 ...

  2. 数字证书及在WCF中的应用

    一 概念 1.内容 证书的发布机构     证书的有效期     证书所有者(Subject)     签名所使用的算法     指纹以及指纹算法 公钥     私钥 2.存储区 3.有效性 二 作用 ...

  3. wcf中的使用全双工通信(转)

    wcf中的使用全双工通信 wcf中的契约通信默认是请求恢复的方式,当客户端发出请求后,一直到服务端回复时,才可以继续执行下面的代码. 除了使用请求应答方式的通信外,还可以使用全双工.下面给出例子: 1 ...

  4. 谈谈C#中的三个关键词new , virtual , override

    谈谈C#中的三个关键词new , virtual , override C#支持单继承,说到继承就不得不说new,virtual和override这三个关键词,灵活正确的使用这三个关键词,可以使程序结 ...

  5. Silverlight中服务通信方式的选择(WCF、Data Service、Ria Service)

    Silverlight中服务通信方式的选择(WCF.Data Service.Ria Service) 转自 http://www.cnblogs.com/024hi/archive/2011/06/ ...

  6. 在WCF中使用Flag Enumerations

    在WCF中使用Flag Enumerations 请看MSDN示例: [DataContract][Flags] public enum CarFeatures {     None = 0,     ...

  7. 【转】x.509证书在WCF中的应用(CS篇)

    [转自]x.509证书在WCF中的应用(CS篇) 为什么要用x.509证书? WCF的服务端和客户端之间,如 果不作任何安全处理(即服务端的<security mode="None&q ...

  8. 在WCF中实现双工通信(转载)

    首先声明此文章是转载博客园蒋老师之作:http://www.cnblogs.com/artech/archive/2007/03/02/661969.html 双工(Duplex)模式的消息交互方式体 ...

  9. WCF中的序列化[上篇]

    SOA 和Message Windows Communication Foundation (WCF) 是基于面向服务架构(Service Orientation Architecture--SOA) ...

  10. Entity Framework在WCF中序列化的问题

    问题描述 如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生. 接收对 http://localhost:5115/ReService.svc 的 ...

最新文章

  1. 2020年数据科学领域4个最热门的趋势
  2. DVWA安装——一个菜鸟的入门教程
  3. tomact+apache实现web网页动静结合
  4. python 列表生成式_深度好文:Python之列表生成式、生成器、可迭代对象与迭代器(一)...
  5. 转载 用ShadowVolume画模型的影子
  6. PostgreSQL 10.1 手册_部分 II. SQL 语言_第 9 章 函数和操作符_9.22. 子查询表达式
  7. 洛谷——P1311 选择客栈
  8. 如何判断各种手机浏览器?
  9. shell命令xargs
  10. java dsa 私钥_OpenSSL工具 DSA私钥及公钥生成
  11. 大数据时代的图表可视化利器——highcharts,D3和百度的echarts
  12. Solr7部署报错:java.lang.NoSuchMethodError: javax.servlet.ServletInputStream.isFinished()Z
  13. Linux 系统批量安装字体的方法
  14. Spring Boot在使用Gradle build命令卡住不动了
  15. Linux下Mysql5.5的Cmake安装以及sphinx结合
  16. 深度学习中Batch size对训练效果的影响
  17. 计算机使用鼠标的课件,鼠标操作ppt
  18. 解决 VS 无法打开包括文件: “XXX.h”: No such file or directory问题
  19. elasticsearch之增删改查与其他基本操作
  20. java实现飞机大战小游戏——————【附素材、源码、逐行注释讲解】

热门文章

  1. mysql 主从 日志_mysql主从复制基于日志复制
  2. 抛开百度、知乎等都找不到连接不上服务器远程桌面的原因
  3. cmake 交叉编译_如何使用CMake编译RTT微内核
  4. elk日志收集系统 linux_ELK 日志分析系统
  5. cognos报表导出excel_17个新增功能点,让报表更惊艳!
  6. 关于四川2019ACM省赛热身赛B题的一个证明
  7. Git-查看远程分支、本地分支、创建分支
  8. Spring的Bean有哪些作用域?
  9. Linux下互斥量加锁与解锁操作的C代码实现
  10. mysql二进制增量备份