Welcome

首页 / 软件开发 / WCF / WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)2012-10-25 博客园 Artech在.NET中,所有的集合都实现了IEnumerable接口,比如Array、Hashtable、ArrayList、Stack、Queue等。有的集合要求元素具有相同的类型,这种集合一般通过泛型的方式定义,它们实现另一个接口IEnumerable<T>(IEnumerable<T>本身继承自IEnumerable),这样的集合有List<T>、Dictionary<TKey,TValue>、Stack<T>、Queue<T>等。基于集合类型的序列化具有一些特殊的规则和行为,在上篇中我们详细介绍了基于泛型数据契约的序列化规则,接下来我们介绍基于集合对象的序列化,以及基于集合类型的服务操作。

一、IEnumerable<T>、Array与IList<T>

一个集合对象能够被序列化的前提是集合中的每个元素都能被序列化,也就是要求元素的类型是一个数据契约(或者是应用了SerialiableAttribute特性)。虽然集合具有各种各样的表现形式,由于其本质就是一组对象的组合,DataContractSerializer在对它们进行序列化的时候,采用的序列化规则和序列化过程中表现出来的行为是相似的。比如我们现在需要通过DataContractSerializer序列化一个Customer对象的集合,Customer类型定义如下。

 1: namespace Artech.DataContractSerializerDemos
2: {
3: [DataContract(Namespace="http://www.artech.com/")]
4: public class Customer
5: {
6: [DataMember(Order = 1)]
7: public Guid ID
8: { get; set; }
9:
10: [DataMember(Order=2)]
11: public string Name
12: { get; set; }
13:
14: [DataMember(Order = 3)]
15: public string Phone
16: { get; set; }
17:
18: [DataMember(Order = 4)]
19: public string CompanyAddress
20: { get; set; }
21: }
22: }
现在我通过我们前面定义的范型Serialize<T>对以下3种不同类型的集合对象进行序列化:IEnumerable<Customer>、IList<Cusomter>和Customer[]。

 1: Customer customerFoo = new Customer
2:{
3:ID = Guid.NewGuid(),
4:Name = "Foo",
5:Phone = "8888-88888888",
6:CompanyAddress = "#9981, West Sichuan Rd, Xian Shanxi Province"
7:};
8:
9: Customer customerBar = new Customer
10: {
11: ID = Guid.NewGuid(),
12: Name = "Bar",
13: Phone = "9999-99999999",
14: CompanyAddress = "#3721, Taishan Rd, Jinan ShanDong Province"
15: };
16: Customer[] customerArray = new Customer[] { customerFoo, customerBar };
17: IEnumerable<Customer> customerCollection = customerArray;
18: IList<Customer> customerList = customerArray.ToList<Customer>();
19:
20: Serialize<Customer[]>(customerArray, @"E:Customer.Array.xml");
21: Serialize<IEnumerable<Customer>>( customerCollection, @"E:Customer.GenericIEnumerable.xml");
22: Serialize<IList<Customer>>( customerList, @"E:Customer.GenericIList.xml);
我们最终发现,虽然创建DataContractSerializer对象使用的类型不一样,但是最终序列化生成出来的XML却是完全一样的,也就是说DataContractSerializer在序列化这3种类型对象时,采用完全一样的序列化规则。从下面的XML的结构和内容中,我们可以总结出下面3条规则:

根节点的名称以ArrayOf为前缀,后面紧跟集合元素类型对应的数据契约名称;

集合元素对象用数据契约的命名空间作为整个集合契约的命名空间;

每个元素对象按照其数据契约定义进行序列化。

 1: <ArrayOfCustomer xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.artech.com/">
2: <Customer>
3: <ID>8baed181-bcbc-493d-8592-3e08fd5ad1cf</ID>
4: <Name>Foo</Name>
5: <Phone>8888-88888888</Phone>
6: <CompanyAddress>#9981, West Sichuan Rd, Xian Shanxi Province</CompanyAddress>
7: </Customer>
8: <Customer>
9: <ID>2fca9719-4120-430c-9dc2-3ef9dc7dffb1</ID>
10: <Name>Bar</Name>
11: <Phone>9999-99999999</Phone>
12: <CompanyAddress>#3721, Taishan Rd, Jinan ShanDong Province</CompanyAddress>
13: </Customer>
14: </ArrayOfCustomer>
我们从根节点的名称ArrayOfCustomer,可以看出WCF将这个3个类型的对象IEnumerable<Customer>、IList<Cusomter>和Customer[]都作为Customer数组了。实际上,如果你在定义服务契约的时候,将某个服务操作的参数类型设为IEnumerable<T>或者<IList>,默认导出生成的服务契约中,相应的参数类型就是数组类型。 比如,在同一个服务契约中,我定义了如下3个操作,他们的参数类型分别为IEnumerable<Customer>、IList<Cusomter>和Customer[]。当客户端通过添加服务引用导出服务契约后,3个操作的参数类型都变成Customer[]了。

 1: [ServiceContract]
2: public interface ICustomerManager
3: {
4: [OperationContract]
5: void AddCustomerArray(Customer[] customers);
6: [OperationContract]
7: void AddCustomerCollection(IEnumerable<Customer> customers);
8: [OperationContract]
9: void AddCustomerList(IList<Customer> customers);
10: }
1: [ServiceContract]
2: [ServiceContract]
3: public interface ICustomerManager
4: {
5: [OperationContract]
6: void AddCustomerArray(Customer[] customers);
7: [OperationContract]
8: void AddCustomerCollection(Customer[] customers);
9: [OperationContract]
10: void AddCustomerList(Customer[] customers);
11: }