在 WCF 服务中,WCF 的通信可以理解为一种消息交换,消息交换就需要有契约使交换的双方达成一致理解,服务契约的操作描述了需要采用的消息交换模式和消息结构,数据契约就用来规范整个通信过程的消息结构。
一、数据契约特性(DataContractAttribute)和数据成员特性(DataMemberAttribute)
在 WCF 中定义了用在类(class)、枚举(enum)和结构(struct)上的 System.Runtime.Serialization.DataContractAttribute 特性,它标识类型为数据契约,DataContractAttribute 是不可以继承的,因此所有作为数据契约的类型都必须应用该特性。DataContractAttribute 中只定义了3个属性成员:是否保持对象现有的引用结构(IsReference)、数据契约的名称(Name)、数据契约的命名空间(Namespace)。
数据契约中的成员采用显示选择机制,只用应用了 System.Runtime.Serialization.DataMemberAttribute 特性的属性和字段才可以成为数据成员。DataMemberAttribute 只能应用到属性和字段上,它属性Order用来控制数据成员序列化时的排序。
数据契约类型声明:
1: [DataContract(Namespace = "http://www.ainote.cn")]
2: public class Customer
3: {
4: [DataMember(Order = 1)]
5: public string FirstName { get; set; }
6: [DataMember(Order = 2)]
7: public string LastName { get; set; }
8: [DataMember(Order = 3)]
9: public string Gender { get; set; }
10: }
二、数据契约序列化器(DataContractSerializer)
WCF 中针对数据契约定义了有一个 System.Runtime.Serialization.DataContractSerializer 序列化器。在 DataContractSerializer 中通过 WriteObject 和ReadObject 两个方法完成序列化和反序列化,它具有5个重要的属性:用于控制序列化、反序列化和契约的导入和导出的数据契约代理类对象(DataContractSurrogate)、用于解决通信双方契约不一致时数据传送和回传过程中数据丢失的扩展数据对象(IgnoreExtensionDataObject)、明确对象所引用的真实类型的已知类型(KnownTypes)、序列化与反序列化允许的最大对象数(MaxItemInObjectGraph)、是否保持现有对象的引用结构(PreserveObjectReferences)。在DataContractSerializer 的构造函数中对应上述属性都有相应的参数,通过参数控制序列化时,数据的接口和类型等。
在使用 DataContractSerializer 类型基于接口和基类进行序列化时,需要通过构造函数指定对象所引用的真实类型的已知类型(KnownTypes)。在数据契约中通过应用 System.Runtime.Serialization.KnownTypeAttribute 特性在数据契约的类型上指定已知类型,KnownTypeAttribute 可以通过两种方式设置已知类型:直接指定已知类型和通过返回类型实现了 IEnumerable<Type> 的静态无参方法指定已知类型。
1: [DataContract]
2: //两种设置已知类型序列化的方式
3: // [KnownType(typeof(Order))]
4: [KnownType("GetknownTypes")]
5: public class OrderBase:IOrder
6: {
7: public static IEnumerable<Type> GetknownTypes() {
8: yield return typeof(Order);
9: }
10: [DataMember]
11: public Guid OrderID { get; set; }
12: [DataMember]
13: public DateTime Date { get; set; }
14: [DataMember]
15: public string Customer { get; set; }
16: [DataMember]
17: public string ShipAddress { get; set; }
18: public double TotalPrice { get; set; }
19: }
数据契约代理 IDataContractSurrogate 接口定义了3个于序列化有关的方法:GetDataContractType-获取用于序列化、反序列化或数据导入、导出的数据契约类型;GetObjectToSerialize -在序列化之前获取序列化的对象;GetDeserializedObject-完成反序列化时获得被反序列化生成的对象。
1: public class UserDataContractSurrogate:IDataContractSurrogate
2: {
3: public Type GetDataContractType(Type type) {
4: if (type==typeof(Contact))
5: {
6: return typeof(Customer);
7: }
8: return type;
9: }
10: public object GetDeserializedObject(object obj, Type targetType) {
11: Customer customer = obj as Customer;
12: if (customer==null)
13: {
14: return obj;
15: }
16: return new Contact {
17: PersonName=customer.FirstName+" "+customer.LastName,
18: Sex=customer.Gender
19: };
20: }
21: public object GetObjectToSerialize(object obj, Type targetType)
22: {
23: Contact contact = obj as Contact;
24: if (contact == null)
25: {
26: return obj;
27: }
28: return new Customer
29: {
30: FirstName = contact.PersonName.Split(" ".ToCharArray())[0],
31: LastName = contact.PersonName.Split(" ".ToCharArray())[1],
32: Gender = contact.Sex
33: };
34: //其他方法
35: }
三、集合数据契约
泛型数据契约和集合数据契约
在 WCF 泛型类型也可以定义成数据契约,泛型数据契约序列化时它根节点名称格式为:{类型名称}+Of+{泛型实参类型1}+{泛型实参类型2}...+{哈希值},也可以通过DataContractAttribute 的Name属性指定一个唯一的名称。
对于实现了IEnumerable 或 IEnumerable<T> 集合类型,如果他的元素类型是数据契约,该集合为数据契约集合。数据契约集合序列化:
1: static void DataContractList()
2: {
3: Person person1 = new Person
4: {
5: ID = Guid.NewGuid(),
6: Name = "Stone",
7: Phone = "18080856212",
8: Address = "四川 成都"
9: };
10: Person person2 = new Person
11: {
12: ID = Guid.NewGuid(),
13: Name = "Peng",
14: Phone = "13540859823",
15: Address = "四川 成都 高新区"
16:
17: };
18: Person[] persons = new Person[] { person1, person2 };
19: string path = @"E:\CSharp\Stone.Serialization.Client\Test\";
20: //Root 节点名称为ArrayOfPerson,ArrayOf+{类型名称}
21: Serialize<Person[]>(persons, path + "PersonArray.xml");
22: Serialize<IEnumerable<Person>>(persons, path + "PersonEnumerable.xml");
23: Serialize<IList<Person>>(persons, path + "PersonList.xml");
24:
25: //IEnumerable和IList 序列化需要指定已知类型,Root节点名称为ArrayOfanyType,ArrayOf+Type
26: Serialize<IEnumerable>(persons, path + "Person.IEnumerable.xml", true, typeof(Person));
27: Serialize<IList>(persons, path + "Person.IList.xml", true, typeof(Person));
28: }
代码中的前3种类型方式序列化后的数据结构一致,后两种的数据结构一致:
前3种类型方式序列化后XML:
1: <ArrayOfPerson xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.ainote.cn">
2: <Person>
3: <ID>cb824c10-13ab-430a-abd0-c66c4df7abc3</ID>
4: <Name>Stone</Name>
5: <Phone>18080856212</Phone>
6: <Address>四川 成都</Address>
7: </Person>
8: <Person>
9: <ID>a3acf0d5-8319-40cb-9b3e-d81bf65477f2</ID>
10: <Name>Peng</Name>
11: <Phone>13540859823</Phone>
12: <Address>四川 成都 高新区</Address>
13: </Person>
14: </ArrayOfPerson>
后2种类型方式序列化后XML:
1: <ArrayOfanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Size="2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
2: <anyType z:Id="2" xmlns:d2p1="http://www.ainote.cn" i:type="d2p1:Person">
3: <d2p1:ID>08a74b87-e78a-407a-9986-28df8c552f8e</d2p1:ID>
4: <d2p1:Name z:Id="3">Stone</d2p1:Name>
5: <d2p1:Phone z:Id="4">18080856212</d2p1:Phone>
6: <d2p1:Address z:Id="5">四川 成都</d2p1:Address>
7: </anyType>
8: <anyType z:Id="6" xmlns:d2p1="http://www.ainote.cn" i:type="d2p1:Person">
9: <d2p1:ID>adfe694f-9350-4cb3-97ef-aa346e5e6333</d2p1:ID>
10: <d2p1:Name z:Id="7">Peng</d2p1:Name>
11: <d2p1:Phone z:Id="8">13540859823</d2p1:Phone>
12: <d2p1:Address z:Id="9">四川 成都 高新区</d2p1:Address>
13: </anyType>
14: </ArrayOfanyType>
通过在类型上应用CollectionDataContract特性,标识数据契约为集合数据契约,集合数据契约类型必须显式定义一个无参构造函数和一个返回类型为 void 的公共Add方法,Add方法的参数为集合数据契约类型的参数。
1: [CollectionDataContract(Namespace="http://www.ainote.cn",ItemName="Add")]
2: public class PersonList : IEnumerable<Person>
3: {
4: public PersonList() { }
5: public void Add(Person per)
6: {
7: this.Persons.Add(per);
8: }
9: //其他成员
10: }
字典数据契约
字典数据契约是一种特殊的集合数据契约,它通过键-值对(key-value)的方式序列化到xml 元素名称,也可以通过CollectionDataContract特性的KeyName 和 ValueName属性指定键-值元素的名称。
字典数据契约序列化:
1: [CollectionDataContract(Name = "PersonDictionary",KeyName="PersonID",
2: ValueName="PersonInfo",Namespace="http://www.ainote.cn")]
3: public class PersonDictionary:Dictionary<Guid,Person>
4: {
5: public PersonDictionary() { }
6: public void Add(Guid key,Person value) {
7: base.Add(key,value);
8: }
9: }
1: <PersonDictionary xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.ainote.cn">
2: <KeyValueOfguidPersonBuvzgkHJ>
3: <PersonID>bbd29a52-da43-4925-af0a-ea45024bca95</PersonID>
4: <PersonInfo>
5: <ID>bbd29a52-da43-4925-af0a-ea45024bca95</ID>
6: <Name>Stone</Name>
7: <Phone>18080856212</Phone>
8: <Address>四川 成都</Address>
9: </PersonInfo>
10: </KeyValueOfguidPersonBuvzgkHJ>
11: <KeyValueOfguidPersonBuvzgkHJ>
12: <PersonID>cfbf233f-9a83-48a3-af8f-8934e42d7846</PersonID>
13: <PersonInfo>
14: <ID>cfbf233f-9a83-48a3-af8f-8934e42d7846</ID>
15: <Name>Peng</Name>
16: <Phone>13540859823</Phone>
17: <Address>四川 成都 高新区</Address>
18: </PersonInfo>
19: </KeyValueOfguidPersonBuvzgkHJ>
20: </PersonDictionary>
还可以通过实现了IDictionary或IDictionary<TKey,TValue> 接口的类型定义值为数据契约类型的数据契约字典。数据契约字典序列化:
1: static void DataContractDictionaryTest()
2: {
3: Person person1 = new Person
4: {
5: ID = Guid.NewGuid(),
6: Name = "Stone",
7: Phone = "18080856212",
8: Address = "四川 成都"
9: };
10: Person person2 = new Person
11: {
12: ID = Guid.NewGuid(),
13: Name = "Peng",
14: Phone = "13540859823",
15: Address = "四川 成都 高新区"
16:
17: };
18:
21: Dictionary<Guid, Person> dic = new Dictionary<Guid, Person>();
22: dic.Add(person1.ID,person1);
23: dic.Add(person2.ID, person2);
24: string path = @"E:\CSharp\Stone.Serialization.Client\Test\";
25: // 数据契约字典序列化
27: Serialize<IDictionary<Guid, Person>>(dic, path + "Person.DataContractDictionary.xml", true, typeof(Person));
28: }
1: <ArrayOfKeyValueOfguidPersonBuvzgkHJ xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Size="2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
2: <KeyValueOfguidPersonBuvzgkHJ>
3: <Key>a3becf2a-f4b7-44c2-bf86-e707ea329ccb</Key>
4: <Value xmlns:d3p1="http://www.ainote.cn" z:Id="2">
5: <d3p1:ID>a3becf2a-f4b7-44c2-bf86-e707ea329ccb</d3p1:ID>
6: <d3p1:Name z:Id="3">Stone</d3p1:Name>
7: <d3p1:Phone z:Id="4">18080856212</d3p1:Phone>
8: <d3p1:Address z:Id="5">四川 成都</d3p1:Address>
9: </Value>
10: </KeyValueOfguidPersonBuvzgkHJ>
11: <KeyValueOfguidPersonBuvzgkHJ>
12: <Key>ada2125d-634d-4c2f-afb0-231347898a77</Key>
13: <Value xmlns:d3p1="http://www.ainote.cn" z:Id="6">
14: <d3p1:ID>ada2125d-634d-4c2f-afb0-231347898a77</d3p1:ID>
15: <d3p1:Name z:Id="7">Peng</d3p1:Name>
16: <d3p1:Phone z:Id="8">13540859823</d3p1:Phone>
17: <d3p1:Address z:Id="9">四川 成都 高新区</d3p1:Address>
18: </Value>
19: </KeyValueOfguidPersonBuvzgkHJ>
20: </ArrayOfKeyValueOfguidPersonBuvzgkHJ>
数据契约的核心是消息的序列化与反序列化,通过通过序列化器控制消息序列化和反序列化后的数据结构。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。