关闭

数据契约:CLR数据类型和与平台无关表示形式的转换

941人阅读 评论(0) 收藏 举报

来源:Bruce Zhang''s Blog - 博客园

  本书的第3章主要讲解了有关数据契约的知识。“从抽象层面看,WCF能够托管CLR类型(接口和类)并将它们公开为服务,也能够以本地CLR接口和类的方式使用服务。WCF服务的操作接收和返回诸如int和string的CLR类型,WCF客户端则传递和处理返回的CLR类型。然而,CLR类型却属于.NET的特定技术。由于面向服务的一个核心原则就是在跨越服务边界时,服务不能够暴露它们的实现技术。因此,不管客户端采用了何种技术,它都能够与服务交互。显然,这就意味着WCF不允许在跨越服务边界时公开CLR数据类型。我们需要找到一种办法,实现CLR数据类型与标准的与平台无关的表示形式之间的转换。这样的表示形式就是基于XML的样式或信息集(Infoset)。此外,服务需要一种正式的方法声明两者之间的转换。这个方法就是本章所要介绍的主题——数据契约。本章的第一部分介绍了数据契约启用类型封送(Type Marshaling)与转换的方法,以及如何通过基础架构处理类的层级与数据契约的版本控制。第二部分则介绍了如何将不同的.NET类型,例如枚举、委托、数据表以及集合,作为数据契约使用。”

  数据契约是服务之间传递的数据。由于必须支持跨进程,乃至于跨机器的传递,WCF必须对数据进行特殊的处理,否则无法实现数据的传递。在.NET Remoting与Web Service中,对数据的处理方式通常是利用序列化的方法,WCF同样沿袭了这一做法,但为了更好的体现面向服务的特质,又特别引入了数据契约(DataContract)。此外,WCF还引入了消息契约(MessageContract),但本书没有介绍。

  WCF的序列化使用了.NET平台自身支持的序列化机制,因此这里不再重复。
.NET提供的序列化机制虽然足以应付SOA的要求,但仍然存在许多不足之处。本书总结了Serializable的缺陷:“Serializable所指代的涵义是类型的所有成员都是可序列化的,这些成员是组成类型数据样式的一部分。然而,更好的方式是能够提供一种明确参与(Opt-In)途径,只有那些契约的开发者明确包含的成员才应该放到数据契约中。Serializable特性强制要求数据类型是可序列化的,从而使得类型可以被用作契约操作的参数,但它却无法实现类型的服务特性(具有成为WCF操作参数的能力)与序列化能力之间的职责分离。Serializalbe特性不支持类型名和成员名的别名,也无法将一个新类型映射为预定义的数据契约。由于Serializable特性可以直接操作成员字段,使得封装了字段访问的属性形同虚设。访问字段的最好办法是通过属性添加它们的值,而Serializable却破坏了属性的封装性。最后,Serializable特性并没有直接支持版本控制(Versioning),而版本控制的信息却是格式器期望获取的。无疑,它导致了版本控制的处理变得举步维艰。”

  WCF提供的数据契约DataContract基本上解决了以上的问题。通常,DataContract必须与DataMember结合使用。只有应用了DataMember特性的属性才被公开到元数据中。虽然DataMember特性也可以应用到对象的字段上,但WCF并不推荐这样做,原因与类的设计原则相同。

  数据契约与服务契约相似,数据成员或数据契约的访问限定与WCF之间并没有因果关系。数据契约完全可以包含私有数据成员等内部类型:
[DataContract]
struct Contact
{
[DataMember]
string m_FirstName;
[DataMember]
string m_LastName;
}
  即使DataMember特性被直接应用到字段上,在导入的客户端定义仍然会以属性来表示。如下的数据契约定义:
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;

[DataMember]
public string LastName;
}
   导入的客户端定义为:
[DataContract]
public partial struct Contact
{
string FirstNameField;
string LastNameField;
[DataMember]
public string FirstName
{
get
{
return FirstNameField;
}
set
{
FirstNameField = value;
}
}
[DataMember]
public string LastName
{
get
{
return LastNameField;
}
set
{
LastNameField = value;
}
}
}
  它会将字段名作为属性名,而导入的定义中,则在属性名后加上Field后缀作为字段名。但我们也可以手工修改客户端的定义。

  如果数据契约的数据成员为私有的,导入的客户端定义会自动修改为公有的。“当DataMember特性应用到属性上时(不管是服务还是客户端),该属性必须具有get和set访问器。如果没有,在调用时就会抛出InvalidDataContractException异常。因为当属性自身就是数据成员时,WCF会在序列化和反序列化时使用该属性,使开发者能够将定制逻辑应用到属性中。”

  “不要将DataMember特性既应用到属性上,又应用到相对应的字段上,这会导致导入的成员定义重复。”
  如果服务端的数据被标记为Serializable特性,在导入这样的定义时,会使用DataContract。而且“对于每一个可序列化的成员,不管是公有的还是私有的,都是数据成员。”

  传统的格式器不能序列化只标记了DataContract特性的类型。要序列化这样的类型,必须同时应用DataContract特性和Serializable特性。对于如此类型生成的传输型表示形式(Wire Representation),就好似仅仅应用了DataContract特性一般,同时,我们仍然需要为成员添加DataMember特性。

  在WCF的数据契约中,很明显地体现出WCF还不能够完全支持面向对象的设计思想。在第2章对服务契约的描述中,对契约的继承层级的处理方式来看,已经体现了这一缺陷的端倪。而对于数据契约而言,更是进一步暴露了这样的缺陷。 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:686785次
    • 积分:8088
    • 等级:
    • 排名:第2487名
    • 原创:3篇
    • 转载:584篇
    • 译文:0篇
    • 评论:129条
    最新评论