• 数据契约


         WCF能够托管CLR类型,并将他们公开为服务,也能够以本地CLR的方式来使用服务。WCF服务的操作接收和返回CLR的类型,WCF客户端则传递和处理返回的CLR类型。CLR类型是.net的概念。由于面向服务的一个核心原则就是在跨越服务边界时,服务不能够暴露他们的实现技术。这就意味着WCF不允许在跨越服务边界是公开CLR的数据类型。因此,需要找到一种方法,实现CLR数据类型和标准的平台无关的表示形式之间的转换。这样的表示形式是基于XML的样式或信息集(Infoset)。实现这两者之间的转换的方法就是数据契约(DataContract)。

    .NET序列化

         在本地使用的对象和引用等都属于CLR的概念,无法实现与WCF服务操作之间的传递。解决办法就是将对象封送(Marshaling)其它的平台和技术。在将对象作为参数进行传递时,需要发送的是对象的状态,然后接收端再将它转化为本地的表示形式 。这种传递状态的方式称为按值封送(Marshaling by value)。执行按值封送的最简单办法是利用大多数平台(包括.net)自身提供的序列化技术。

         .NET通过反射技术自动的实现了对象的序列化和反序列化。在实现中,是否执行序列化应该有类的开发者决定。.NET通过Serializable属性来支持类的序列化。如果类中含有非序列化成员,通常情况下,非序列化成员就是那些需要执行特殊的初始化操作的引用类型。解决这个问题的办法是为这样的成员标记NonSerialized特性,然后在反序列化时,采取定制步骤对它们进行初始化。

    格式器

         .NET为类型的序列化和反序列化提供了两种格式器。BinaryFormatter会将类型序列化为二进制格式。由于不需要解析,这种格式器能够快速的执行序列化和反序列化操作。SoapFormatter则使用了.NET特定的SOAPXML格式,在序列化时,它引入了组合格式,然后在反序列化时对组合格式进行解析,完成转换。.NET格式器并不能满足面向服务的交互。因此WCF提供了自己的格式器DataContractSerializier。它能够共享数据契约而不是基本的类型信息。

    序列化数据契约

         当一个服务操作接收和返回任意类型和参数时,WCF会使用DataContractSerializier对参数进行序列化和反序列化。这意味着只要各参与方拥有相同的数据契约的定义,我们就可以将可序列化类型作为参数或返回值进行传递。

         所有的.NET内建的基本类型都是可序列化的。如果需要在操作中使用定制的类型参数,需要满足两个条件:首先,类型必须是可序列化的(Serializable)。其次,客户端与服务都拥有该类型的本地定义,而且该类型应该具有相同的数据样式。

         Serializable所指代的涵义是类型的所有成员都是可序列化的。更好的方式是能够提供一种明确参与(Opt-In)的途径,只有那些契约的开发这明确包含的成员才应该放到数据契约中。

         Serializable有以下缺点:他无法实现类型的类型的服务特性(具有成为WCF操作参数的能力)与序列化能力之间的职责分离。不支持类型名和成员名的别名。无法将一个新类型映射为预定义的数据契约。它直接操作成员字段,破坏了属性的封装性。没有直接支持版本控制。

         针对Serializable的缺点,WCF提供了DataContract和DataMember特性。DataContract应用到类级,DataMember应用到成员级。

    客户端导入数据契约

         当客户端导入数据契约的定义时,它使用的是与服务端等效的定义,但不是同一个数据契约。导入的定义会保留类或结构原来的类型名。两端使用的数据契约可以是等效的而不是同一个。当然也可以使用不等效的,客户端对它自己的契约的控制和配置,与服务无关。

         在导入定义中,服务端原来的私有字段或属性在客户端都被定义成了共有属性。

         如果将DataMember应用到属性上,该属性必须具有get和set访问器。否则会抛出InvalidDataContractException异常。不要将DataMember同时应用到字段和属性上,会导致重复定义。

    数据契约的等效性

          如果两个数据契约具有相同的传输型表示形式,就可以认为是等效的。等效的数据契约包括:类型定义相同,或者指向两个不同类型的数据契约,他们的契约名以及成员的名称完全相同。等效的数据契约可以互换,因为WCF允许服务操作与它的数据契约等效的数据契约。

         定义一个等效数据契约的最常用方法是使用DataContract和DataMember的Name属性,将数据契约映射为另一个数据契约。

    枚举与数据契约

    枚举类型的定义总是支持序列化的,因此,不必应用DataContract特性。数据契约隐式的包含了枚举对象的所有值。如果需要将枚举的特定值排除在外。则需要显示使用DataContract和EnumMember特性。如:

    Code

    委托与数据契约

         在序列化一个包含了委托成员变量的对象时,委托的内部调用列表也会被序列化。列表中的结构是本地的,而且我们也不能保证列表中的对象都是可序列化的。这回导致序列化操作时失败。因此,如果数据契约时可序列化的。应该将委托排除在外,不使用DataMember属性。

    数据集(表)与数据契约

         在.NET中,与数据库本地的交互是通过ADO.NET的数据集或数据表类型。DataSet和DataTable类型是可序列化的,因为他们的定义标记了Serializable属性。我们可以定义有效的服务契约,接收或返回数据集(表)。但是数据行(row)不是序列化的,因此不能直接作为参数。

         使用数据集(表)样式复杂,而且可能暴露内部的数据结构,同样对数据库样式的修改会影响到客户端。所以更好的做法是暴露数据的操作而非数据本身。如果要传递数据本身,最好的方法是使用与数据结构无关的类型,如数组。

    泛型

         不要定义包含了泛型类型参数的数据契约。泛型是.NET的特定技术,使用他们可能会与WCF的面向服务端本质发生冲突。但是,我们仍然可以在数据契约中使用限定的泛型类型,只要在服务契约中指定了类型参数,并且指定的类型参数具有有效的数据类型。如:

    Code

    集合

         在.NET中,所有类型的集合都实现了IEnumerable或IEnumerable<T>接口。一个数据契约的数据成员可以是一个集合类型,服务契约也可以定义直接与集合交互的操作。因为.NET集合是.NET特有的,所以WCF不能在服务中公开他们,WCF专门为集合提供了封送原则。在服务中,不管使用那一种结合接口,它的传输形式都使用了数组。如:

    Code

    导出结果为:

    Code

          在.NET3.5后,我们可以通过手动或者使用服务引用向导来实现客户端映射样式的选择。

         可以在Add Service Reference 时选择Advanced界面,弹出Service Reference Setting界面。可以在该界面中选择映射的type。

  • 相关阅读:
    如何将 Python 程序打包成 .exe 文件?
    无穷滚动(Infinite scroll)的实现原理
    图片延迟加载(lazyload)的实现原理
    Java 基础 -- 泛型、集合、IO、反射
    Ubuntu on win10
    让自己少走点弯路
    使用MongoDB 记录业务日志
    19个JavaScript数组常用方法总结
    Kubernetes 使用Nginx-Ingress实现蓝绿发布/金丝雀发布/AB测试
    官方golang包管理神器
  • 原文地址:https://www.cnblogs.com/jyz/p/1300592.html
Copyright © 2020-2023  润新知