WCF之数据契约

  • 从抽象层面看,WCF能够托管CLR类型(接口和类)并将它们公开为服务,也能够以本地CLR接口和类的方式使用服务。然而,CLR类型却属于.NET的特定技术。由于面向服务的一个核心原则就是在跨越服务边界时,服务不能够暴露它们的实现技术。因此,不管客户端采用了何种技术,它都能够与服务交互。使用基于XML的样式或信息集(Infoset)实现CLR数据类型与标准的与平台无关的表示形式之间的转换。此外,服务需要一种正式的方法声明两者之间的转换,这就是契约.
  • Serializable所指代的涵义是类型的所有成员都是可序列化的,这些成员是组成类型数据样式的一部分。
    • 然而,更好的方式是能够提供一种明确参与(Opt-In)途径,只有那些契约的开发者明确包含的成员才应该放到数据契约中。
    • Serializable特性强制要求数据类型是可序列化的,从而使得类型可以被用作契约操作的参数,但它却无法实现类型的服务特性(具有成为WCF操作参数的能力)与序列化能力之间的职责分离。
    • Serializalbe特性不支持类型名和成员名的别名,也无法将一个新类型映射为预定义的数据契约。
    • 由于Serializable特性可以直接操作成员字段,使得封装了字段访问的属性形同虚设。
    • 访问字段的最好办法是通过属性添加它们的值,而Serializable却破坏了属性的封装性。
    • 最后,Serializable特性并没有直接支持版本控制(Versioning),而版本控制的信息却是格式器期望获取的.
    • WCF提供的数据契约DataContract基本上解决了以上的问题。通常,DataContract必须与DataMember结合使用。只有应用了DataMember特性的属性才被公开到元数据中。虽然DataMember特性也可以应用到对象的字段上,但WCF并不推荐这样做. 即使DataMember特性被直接应用到字段上,在导入的客户端定义仍然会以属性来表示。它会将字段名作为属性名,而导入的定义中,则在属性名后加上Field后缀作为字段名。但我们也可以手工修改客户端的定义.
    • 如果数据契约的数据成员为私有的,导入的客户端定义会自动修改为公有的DataMember特性应用到属性上时(不管是服务还是客户端),该属性必须具有getset访问器。如果没有,在调用时就会抛出InvalidDataContractException异常。因为当属性自身就是数据成员时,WCF会在序列化和反序列化时使用该属性,使开发者能够将定制逻辑应用到属性中。不要将DataMember特性既应用到属性上,又应用到相对应的字段上,这会导致导入的成员定义重复.
    • 如果服务端的数据被标记为Serializable特性,在导入这样的定义时,会使用DataContract。而且“对于每一个可序列化的成员,不管是公有的还是私有的,都是数据成员。” 

  • WCF不完全支持面向对象: 首先WCF并不支持Liskov替换原则(LSP),“默认情况下,我们不能用数据契约的子类去替换基类。”虽然WCF引入了Known Types(已知类型)来解决这一问题,这样的设计无疑会引入父类与子类之间的耦合度。因为在我们设计父类的时候,就必须事先知道子类的定义当我们需要扩展子类时,还需要修改父类的定义。WCF引入的服务已知类型,比较已知类型而言,有一定程度的改善。因为它可以将父类与子类在层级上的耦合度缩小到方法级上。但这样的耦合,依然是不可接受的. 当然,服务已知类型也可以应用到契约接口上,此时,该契约以及实现该契约的所有服务包含的所有操作都能够接收已知的子类.

    • 为了解决这一问题,WCF提供了配置已知类型的方法. 我们配置的已知类型必须是类型的fullname。包括命名空间、版本号、Culture. 如果已知类型对于另一个程序集而言是内部(internal)类型,要添加一个已知类型,只有使用配置文件声明它.
  • WCF对于枚举、委托、DataSet和DataTable、泛型、集合的支持有限.
    • 枚举:自身支持序列化,所以默认就是在数据契约里,当只需要其一部分被序列化时,使用EnumMember表示出需要的数据.在客户端只有被EnumMember的数据才出现在枚举定义里.
    • 委托:WCF对委托以及事件的支持都不够好。这是因为委托的内部调用列表的具体结构是本地的,客户端或服务无法跨服务边界共享委托列表的结构。此外,我们不能保证内部列表中的目标对象都是可序列化的,或者都是有效的数据契约。这会导致序列化的操作时而成功,时而失败。因此,最佳实践是不要将委托成员或事件作为数据契约的一部分.
    • 数据集和数据表: 是可以序列化的.在服务契约中使用该类型,代理类中只会有一个定义,而没有任何代码.我们可以手工修改. 然而,WCF的最佳实践则是避免使用DataTable和DataSet,以及使用DataTable和DataSet的类型安全的子类。这种方式过于繁琐。而且,这些数据访问类型都是特定的.NET类型。在序列化时,它们生成的数据契约样式过于复杂,很难与其它平台进行交互。在服务契约中使用数据表或者数据集还存在一个缺陷,那就是它可能暴露内部的数据结构。同时,将来对数据库样式的修改会影响到客户端。虽然在应用程序内部可以传递数据表,但如果是跨越应用程序或公有的服务边界发送数据表,却并非一个好的主意。通常情况下,更好的做法是暴露数据的操作而非数据本身。最好的做法是将DataTable转换为数组类型.
    • 泛型: 不能在数据契约里定义泛型,但是,可以在服务段使用泛型,但是在生成的数据契约定义中,泛型被具体的类型(<原有名>Of<类型参数名><哈希值>)代替.WCF还支持将自定义类型作为泛型参数。此外,还可以通过数据契约的Name属性为导出的数据契约指定不同的名字.
    • 集合:WCF支持泛型集合、定制集合.但是有很多限制.对于自定义的集合应该采用
      • [KnownType(typeof(Document))]
      • [CollectionDataContract]
      •  [Serializable]
      •  public class DocumentList:IList<Document>
      • 这样,客户端应用程序可以直接使用数据契约,仍然能够识别.
posted @ 2014-06-11 11:38  robynhan  阅读(418)  评论(0编辑  收藏  举报