程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF技術剖析之十四:泛型數據契約和集合數據契約(上篇)

WCF技術剖析之十四:泛型數據契約和集合數據契約(上篇)

編輯:關於.NET

在.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
2: {
3: [DataContract(Namespace="http://www.artech.com/")]
4: public class Bill<BillHeader, BillDetail>
5: {
6: [DataMember(Order = 1)]
7: public BillHeader Header
8: { get; set; }
9:
10: [DataMember(Order = 2)]
11: public BillDetail[] Details
12: { get; set; }
13: }
14: }

然後我們定義用於描述訂單單據的報頭和明細的類型:OrderBillHeader和OrderBillDetail。OrderBillHeader描述定單的總體信息,OrderBillDetail實際上表示訂單中每一個產品的ID、單價和數量。

   1: namespace Artech.DataContractSerializerDemos
2: {
3: [DataContract(Namespace="http://www.artech.com/")]
4: public class OrderBillHeader
5: {
6: [DataMember]
7: public Guid OrderID
8: { get; set; }
9:
10: [DataMember]
11: public DateTime Date
12: { get; set; }
13:
14: [DataMember]
15: public string Customer
16: { get; set; }
17: }
18:
19: [DataContract(Namespace="http://www.artech.com/")]
20: public class OrderBillDetail
21: {
22: [DataMember]
23: public Guid ProductID
24: { get; set; }
25:
26: [DataMember]
27: public int Quantity
28: { get; set; }
29:
30: [DataMember]
31: public double UnitPrice
32: { get; set; }
33: }
34: }

通過下面一個方法創建泛型類型Bill<BillHeader, BillDetail>對象,泛型類型指定為上面定義的OrderBillHeader和OrderBillDetail。

   1: private static Bill<OrderBillHeader, OrderBillDetail> CreateOrderBill()
2: {
3: OrderBillHeader header = new OrderBillHeader
4: {
5: OrderID = Guid.NewGuid(),
6: Date = DateTime.Today,
7: Customer = "Foo"
8: };
9:
10: IList<OrderBillDetail> details = new List<OrderBillDetail>();
11: OrderBillDetail detail = new OrderBillDetail
12: {
13: ProductID = Guid.NewGuid(),
14: Quantity = 20,
15: UnitPrice = 888
16: };
17: details.Add(detail);
18: detail = new OrderBillDetail
19: {
20: ProductID = Guid.NewGuid(),
21: Quantity = 10,
22: UnitPrice = 9999
23: };
24: details.Add(detail);
25:
26:
27: Bill<OrderBillHeader, OrderBillDetail> orderBill = new Bill<OrderBillHeader, OrderBillDetail>()
28: {
29: Header = header,
30: Details = details.ToArray<OrderBillDetail>()
31: };
32: return orderBill;
33: }

借助在《WCF技術剖析之十二:數據契約(Data Contract)和數據契約序列化器(DataContractSerializer)》定義的Serialize<T>輔助方法,我們對創建Bill<OrderBillHeader, OrderBillDetail>對象進行序列化。最終對象將被序列化成如下的XML。

   1: Bill<OrderBillHeader, OrderBillDetail> orderBill = CreateOrderBill();
2: Serialize<Bill<OrderBillHeader, OrderBillDetail>>(orderBill, @"orderbill.xml");
1: <BillOfOrderBillHeaderOrderBillDetail6Of3LqKh xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.artech.com/Artech.DataContractSerializerDemos">
2: <Header>
3: <Customer>NCS</Customer>
4: <Date>2008-12-04T00:00:00+08:00</Date>
5: <OrderID>15a62aae-c955-4bc0-acb6-e171fb9fe085</OrderID>
6: </Header>
7: <Details>
8: <OrderBillDetail>
9: <ProductID>f7679949-938a-40a0-a32a-5dde5c85e55f</ProductID>
10: <Quantity>20</Quantity>
11: <UnitPrice>888</UnitPrice>
12: </OrderBillDetail>
13: <OrderBillDetail>
14: <ProductID>bbd750ff-8b0c-48f5-ab1f-5ad7e51bd420</ProductID>
15: <Quantity>10</Quantity>
16: <UnitPrice>9999</UnitPrice>
17: </OrderBillDetail>
18: </Details>
19: </BillOfOrderBillHeaderOrderBillDetail6Of3LqKh>

XML整體的結構正是我們希望的,關鍵是根節點名稱,也就是數據契約的名稱,“BillOfOrderBillHeaderOrderBillDetail6Of3LqKh”,會讓有些人難以理解。我們仔細分析一下數據契約的名稱,會發現它的組成結構是這樣的:{類型名稱(Bill)}+ Of + {第一個范型參數的類型(OrderBillHeader)} + {第二個范型參數的類型(OrderBillDetail)}+ {哈希值(6Of3LqKh)}。

這裡說泛型參數的類型,實際上是不對的,應該說OrderBillHeader和OrderBillDetail的泛型類型對應的數據契約的名稱。在下面的代碼中。通過 DataContractAttribute特性修改了數據契約的名稱(OrderHeader和OrderDetail),最終的數據契約的名稱將會變成:BillOfOrderHeaderOrderDetail6Of3LqKh。可以看出描述泛型數據契約的部分內容相應地改變了。可能仔細的讀者已經發現了,哈希值部分卻沒有發生變化,依然是“6Of3LqKh”,這是因為這是泛型類型(含命名空間)的哈希值,而不是數據契約名稱的哈希值。所以我們可以將默認的基於泛型類型的命名規則表示成:[類型名稱][范型數據契約名稱1][ 范型數據契約名稱2][…][含命名空間的范型類型哈希值]。

   1: namespace Artech.DataContractSerializerDemos
2: {
3: [DataContract(Name="OrderHeader")]
4: public class OrderBillHeader
5: {
6: //省略成員
7: }
8:
9: [DataContract(Name = "OrderDetail")]
10: public class OrderBillDetail
11: {
12: //省略成員
13: }
14: }

WCF之所以要采用這樣的數據契約命名方式,是為了解決命名沖突,保證數據契約名稱的唯一性。我們說了,面向服務下的數據契約完全沒有泛型的概念,對它來說所有的類型都是“實實在在”的具體類型。對於泛型類型Bill<BillHeader,BillDetail>,不同的BillHeader和BillDetail組合代表不同的數據契約,所以最終的數據契約的名稱需要由自身類型和泛型契約名稱派生出來。由於在定義數據契約的時候,不同的CLR類型可以指定相同的數據契約名稱,所以加上一個基於所有范型類型(含命名空間)的哈希值可以確保數據契約的唯一性。

WCF在進行元數據發布的時候,會自動按照這樣的命名機制創建數據契約,並以XSD的形式發布出來。所以當客戶端導入元數據生成客戶端代碼的時候,生成的等效數據契約的類型名稱就是這個經過拼接的名稱。下面是Bill<OrderBillHeader, OrderBillDetail>導入的形式。

   1: public partial class BillOfOrderBillHeaderOrderBillDetail6Of3LqKh : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged
2: {
3: //省略成員
4: }

三、如何顯式指定契約名稱

如果你能夠確保命名不會發生沖突,你可以通過DataContractAttribute特性的Name屬性對數據契約的名稱進行顯式設置。比如在下面的代碼中,將契約名稱限定為“OrderBill”。不過這樣設置就意味著你假定泛型類型只能表示基於訂單的單據了,這相當於失去了泛型的意義。

   1: namespace Artech.DataContractSerializerDemos
2: {
3: [DataContract(Name="OrderBill")]
4: public class Bill<BillHeader, BillDetail>
5: {
6: //省略成員
7: }
8: }

所以我們可以采用一種動態的設置方式,為數據契約的名稱指定一個模板,使用表示泛型數據契約名稱和泛型類型哈希值的占位符。其中{0}、{1}表示的是范型數據契約的名稱,數字表示相應的范型參數出現的次序,而哈希值則通過{#}表示。所以下面兩種范型數據契約是完全等效的。

   1: namespace Artech.DataContractSerializerDemos
2: {
3: [DataContract]
4: public class Bill<BillHeader, BillDetail>
5: {
6: //省略成員
7: }
8: }
9: namespace Artech.DataContractSerializerDemos
10: {
11: DataContract(Name="BillOf{0}{1}{#}")]
12: public class Bill<BillHeader, BillDetail>
13: {
14: //省略成員
15: }
16: }

注:部分內容節選自《WCF技術剖析(卷1)》第五章:序列化與數據契約(Serialization and Data Contract)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved