[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]在.NET Framework 2.0中,泛型第一次被引入。我们可以定义泛型接口、泛型类型、泛型委托和泛型方法。序列化依赖于真实具体的类型,而泛型则刻意模糊了具体类型概念。而集合代表一组对象的组合,集合具有可迭代(Enumerable)的特性,可以通过某个迭代规则遍历集合中的每一个元素。由于范型类型和集合类型在序列化和反序列化上具有一些特殊的行为和规则,在这篇文章中,我将会对此进行详细介绍。上篇先来说所泛型数据契约。
一、泛型与数据契约
面向对象通过继承实现了代码的重用,而泛型则实现了“算法的重用”。我们定义一种算法,比如排序、搜索、交换、比较或者转换等等,为了实现尽可能的重用,我们并不限定该算法操作对象的具体类型,而通过一个泛型类型来表示。在真正创建范型对象或者调用该方法的时候,才指定其具体的类型。
就实现来说,泛型是CLR和编程语言(或者是基于编程语言的编译器)共同实现的一种特殊机制;就泛型的概念来说,这是面向对象的范畴。而我们现在介绍的数据契约,则属于面向服务的概念。两者具有一些冲突 ,比如面常服务没有继承、重载的概念一样,面向服务同样也无法理解泛型。
但是基于WCF的编程语言是C#、VB.NET这样的完全面向对象的编程语言,而WCF服务却是基于面向服务的。所以,从某种意义上讲,WCF的一个重大的作用就是弥合面向对象编程(OOP)和面向服务架构(SOA)之间的差异。我们现在就来看看WCF做了些什么使我们能够以泛型类型的形式来定义数据契约。
二、泛型数据契约的默认序列化规则
我们首先通过一个简单的例子看看DataContractSerializer是如何序列化一个范型对象的。为此我定义一个泛型类型Bill<BillHeader, BillDetail>,代表一个一般意义上的单据,BillHeader和BillDetail代表单据报头的明细的类型。两个属性Header和Details表示单据报头和明细列表。
1: namespace Artech.DataContractSerializerDemos<!--CRLF-->
2: {
<!--CRLF-->
3: [DataContract(Namespace="http://www.artech.com/")]<!--CRLF-->
4: public class Bill<BillHeader, BillDetail><!--CRLF-->
5: {
<!--CRLF-->
6: [DataMember(Order = 1)]
<!--CRLF-->
7: public BillHeader Header<!--CRLF-->
8: { get; set; }
<!--CRLF-->
9:
<!--CRLF-->
10: [DataMember(Order = 2)]
<!--CRLF-->
11: public BillDetail[] Details<!--CRLF-->
12: { get; set; }
<!--CRLF-->
13: }
<!--CRLF-->
14: }
<!--CRLF-->
然后我们定义用于描述订单单据的报头和明细的类型:OrderBillHeader和OrderBillDetail。OrderBillHeader描述定单的总体信息,OrderBillDetail实际上表示订单中每一个产品的ID、单价和数量。
1: namespace Artech.DataContractSerializerDemos<!--CRLF-->
2: {
<!--CRLF-->
3: [DataContract(Namespace="http://www.artech.com/")]<!--CRLF-->
4: public class OrderBillHeader<!--CRLF-->
5: {
<!--CRLF-->
6: [DataMember]
<!--CRLF-->
7: public Guid OrderID<!--CRLF-->
8: { get; set; }
<!--CRLF-->
9:
<!--CRLF-->
10: [DataMember]
<!--CRLF-->
11: public DateTime Date<!--CRLF-->
12: { get; set; }
<!--CRLF-->
13:
<!--CRLF-->
14: [DataMember]
<!--CRLF-->
15: public string Customer<!--CRLF-->
16: { get; set; }
<!--CRLF-->
17: }
<!--CRLF-->
18:
<!--CRLF-->
19: [DataContract(Namespace="http://www.artech.com/")]<!--CRLF-->
20: public class OrderBillDetail<!--CRLF-->
21: {
<!--CRLF-->
22: [DataMember]
<!--CRLF-->
23: public Guid ProductID<!--CRLF-->
24: { get; set; }
<!--CRLF-->
25:
<!--CRLF-->
26: [DataMember]
<!--CRLF-->
27: public int Quantity<!--CRLF-->
28: { get; set; }
<!--CRLF-->
29:
<!--CRLF-->
30: [DataMember]
<!--CRLF-->
31: public double UnitPrice<!--CRLF-->
32: { get; set; }
<!--CRLF-->
33: }
<!--CRLF-->
34: }
<!--CRLF-->
通过下面一个方法创建泛型类型Bill<BillHeader, BillDetail>对象,泛型类型指定为上面定义的OrderBillHeader和OrderBillDetail。
1: private static Bill<OrderBillHeader, OrderBillDetail> CreateOrderBill()<!--CRLF-->
2: {
<!--CRLF-->
3: OrderBillHeader header = new OrderBillHeader<!--CRLF-->
4: {
<!--CRLF-->
5: OrderID = Guid.NewGuid(),
<!--CRLF-->
6: Date = DateTime.Today,
<!--CRLF-->
7: Customer = "Foo"<!--CRLF-->
8: };
<!--CRLF-->
9:
<!--CRLF-->
10: IList<OrderBillDetail> details = new List<OrderBillDetail>();<!--CRLF-->
11: OrderBillDetail detail = new OrderBillDetail<!--CRLF-->
12: {
<!--CRLF-->
13: ProductID = Guid.NewGuid(),
<!--CRLF-->
14: Quantity = 20,
<!--CRLF-->
15: UnitPrice = 888
<!--CRLF-->
16: };
<!--CRLF-->
17: details.Add(detail);
<!--CRLF-->
18: detail = new OrderBillDetail<!--CRLF-->
19: {
<!--CRLF-->
20: ProductID = Guid.NewGuid(),
<!--CRLF-->
21: Quantity = 10,
<!--CRLF-->
22: UnitPrice = 9999
<!--CRLF-->
23: };
<!--CRLF-->
24: details.Add(detail);
<!--CRLF-->
25:
<!--CRLF-->
26:
<!--CRLF-->
27: Bill<OrderBillHeader, OrderBillDetail> orderBill = new Bill<OrderBillHeader, OrderBillDetail>()<!--CRLF-->
28: {
<!--CRLF-->
29: Header = header,
<!--CRLF-->
30: Details = details.ToArray<OrderBillDetail>()
<!--CRLF-->
31: };
<!--CRLF-->
32: return orderBill;<!--CRLF-->
33: }
<!--CRLF-->
借助在《WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)》定义的Serialize<T>辅助方法,我们对创建Bill<OrderBillHeader, OrderBillDetail>对象进行序列化。最终对象将被序列化成如下的XML。
1: Bill<OrderBillHeader, OrderBillDetail> orderBill = CreateOrderBill();
<!--CRLF-->
2: Serialize<Bill<OrderBillHeader, OrderBillDetail>>(orderBill, @"orderbill.xml");<!--CRLF-->
1: <BillOfOrderBillHeaderOrderBillDetail6Of3LqKh xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.artech.com/Artech.DataContractSerializerDemos"><!--CRLF-->
2: <Header><!--CRLF-->
3: <Customer>NCS</Customer><!--CRLF-->
4: <Date>2008-12-04T00:00:00+08:00</Date><!--CRLF-->
5: <OrderID>15a62aae-c955-4bc0-acb6-e171fb9fe085</OrderID><!--CRLF-->
6: </Header><!--CRLF-->
7: <Details><!--CRLF-->
8: <OrderBillDetail><!--CRLF-->
9: <ProductID>f7679949-938a-40a0-a32a-5dde5c85e55f</ProductID><!--CRLF-->
10: <Quantity>20</Quantity><!--CRLF-->
11: <UnitPrice>888</UnitPrice><!--CRLF-->
12: </OrderBillDetail><!--CRLF-->
13: <OrderBillDetail><!--CRLF-->
14: <ProductID>bbd750ff-8b0c-48f5-ab1f-5ad7e51bd420</ProductID><!--CRLF-->
15: <Quantity>10</Quantity><!--CRLF-->
16: <UnitPrice>9999</UnitPrice><!--CRLF-->
17: </OrderBillDetail><!--CRLF-->
18: </Details><!--CRLF-->
19: </BillOfOrderBillHeaderOrderBillDetail6Of3LqKh><!--CRLF-->
XML整体的结构正是我们希望的,关键是根节点名称,也就是数据契约的名称,“BillOfOrderBillHeaderOrderBillDetail6Of3LqKh”,会让有些人难以理解。我们仔细分析一下数据契约的名称,会发现它的组成结构是这样的:{类型名称(Bill)}+ Of + {第一个范型参数的类型(OrderBillHeader)} + {第二个范型参数的类型(OrderBillDetail)}+ {哈希值(6Of3LqKh)}。
这里说泛型参数的类型,实际上是不对的,应该说OrderBillHeader和OrderBillDetail的泛型类型对应的数据契约的名称。在下面的代码中。通过 DataContractAttribute特性修改了数据契约的名称(OrderHeader和OrderDetail),最终的数据契约的名称将会变成:BillOfOrderHeaderOrderDetail6Of3LqKh。可以看出描述泛型数据契约的部分内容相应地改变了。可能仔细的读者已经发现了,哈希值部分却没有发生变化,依然是“6Of3LqKh”,这是因为这是泛型类型(含命名空间)的哈希值,而不是数据契约名称的哈希值。所以我们可以将默认的基于泛型类型的命名规则表示成:[类型名称][范型数据契约名称1][ 范型数据契约名称2][…][含命名空间的范型类型哈希值]。