SOA 和Message
Windows Communication Foundation (WCF) 是基于面向服務(wù)架構(gòu)(Service Orientation Architecture——SOA)的一種理想的分布式技術(shù)(Distributed Technology), 相信在今后在建立基于SOA企業(yè)級別的解決方案和進行系統(tǒng)集成方面將會大有作為。一個基于SOA結(jié)構(gòu)的互聯(lián)系統(tǒng)(Connected System)通常由若干相互獨立的子系統(tǒng)(Sub-System)組成,這些子系統(tǒng)可能一個獨立的Application,也可能是由若干Application相互集成共同完成一組相關(guān)的任務(wù)的小系統(tǒng)。這些子系統(tǒng)以一種有效的方式組合、集成為我們聽過一種具有綜合功能的解決方案。
在一個基于SOA的分布式系統(tǒng)中,各個子系統(tǒng)相互獨立又相互關(guān)聯(lián)。說它們的相互獨立是因為他們各個都是一個個自治的系統(tǒng)(Autonomous System),可以實行各自的版本策略和部署策略,而這種版本的部署上的變動通常不應(yīng)該引起系統(tǒng)中其他部分的變動。說它們又彼此關(guān)聯(lián),則是因為一個子系統(tǒng)所需要的功能往往由其他某個子系統(tǒng)來實現(xiàn),我們把實現(xiàn)功能的一方稱為Service 的提供者(Provider),把使用Service的一方稱為客戶(Client)。Client依賴于Service的調(diào)用,而不失依賴于Service的實現(xiàn),只要Service的調(diào)用方式?jīng)]有發(fā)生改變,Service的實現(xiàn)變動對于Service的使用者來說是完全透明的。在WCF中,我們把Service的調(diào)用相關(guān)的提取出來即為我們經(jīng)常說的Contract,Service的提供者和Client之間共享的是Service Contract——而不傳統(tǒng)OO概念下的Type。把相對穩(wěn)定的Service Contract和經(jīng)常變動的Service Implementation相互分布早就了我們互聯(lián)系統(tǒng)的松耦合性(Loosely Couple)。
前面我們簡單介紹了SOA系統(tǒng)的基本特征——子系統(tǒng)之間的松耦合性(Loosely Couple);各個子系統(tǒng)的自治性(Autonomous);共享Contract。此外SOA還有其他的一些特征,最重要的一個特征就它是一個基于Message(Message-Based)的系統(tǒng)。子系統(tǒng)之間的相互交互由Message來實現(xiàn)。Client向Service的提供者發(fā)送一個Soap Message來訪為他所需要的Service,Service的提供者監(jiān)聽到來自Client的請求,創(chuàng)建相應(yīng)的Service對象,執(zhí)行相關(guān)的操作,把執(zhí)行的結(jié)果(Result)以Message 的形式發(fā)回給對應(yīng)的Client。所以我們可以說子系統(tǒng)之間的相互交互本質(zhì)上是一種消息的交互過程(Message Exchange)。不同的交互方式對應(yīng)不同的Message Exchange Pattern——MEP。
理解了SO的基本原理,我們來看看WCF,從WCF的全稱來分析——Windows Communication Foundation,顧名思義,他就是解決分布式互聯(lián)系統(tǒng)中各相互獨立的子系統(tǒng)如何交互的問題,換句話說,它實際上就是提供 一個基礎(chǔ)構(gòu)架(Infrastructure)實現(xiàn)Application的通信問題。我們前邊已經(jīng)提到,各個子系統(tǒng)之間是通過XML Message進行交互的,所以我們可以 把WCF看成是一個完全處理XML Message的構(gòu)架,WCF的所有的功能都是圍繞著Message來展開的——如何把一個Service的調(diào)用轉(zhuǎn)或稱一個Message Exchange(Service Contract);如何實現(xiàn)一般的.NET對象和能夠容納于XML Message中的XML Infoset之間的轉(zhuǎn)化(Serialization和Deserialization);如何實現(xiàn)承載數(shù)據(jù)的XML Infoset和能夠用于網(wǎng)絡(luò)傳遞的字節(jié)流(Byte Stream)之間的相互轉(zhuǎn)化(Encoding和Deconding);如何保證置于Message中數(shù)據(jù)的一致性和防止被惡意用戶竊取以及驗證調(diào)用Service和通過Service的合法性(Security:Confidentiality,Integrity,Authentication——CIA);如何保證Message被可靠地被傳達(dá)到所需的地方(Reliable Messaging);以及如何把若干次Service調(diào)用——本質(zhì)上是若干次Message Exchange納入到一個單獨的Conversation(Session Support 和Transaction Support……
在分布式系統(tǒng)中,一個Application與另一個Application之間進行交互,必然需要攜帶數(shù)據(jù)。前面我們說了,系統(tǒng)交互完全是應(yīng)Message的方式進行的,Message是XML,當(dāng)然置于Message中的數(shù)據(jù)也應(yīng)該是XML(XML Infoset)。如何處理這些交互的數(shù)據(jù),我們可能首先想到的就是直接處理XML,我們可以在XML級別通過相關(guān)的XML技術(shù)——XSD,XPath,XSLT來操作數(shù)據(jù)。但是要使我們處理后的XML需要和要求的完全一致,這樣的工作無疑是非??菰锓ξ抖屹M時費力的。而我們最擅長的就是使用.NET對象來封裝我們的數(shù)據(jù)。如何使我們創(chuàng)造的對象能夠有效地轉(zhuǎn)化成結(jié)構(gòu)化的XML Infoset,就是今天我們要講的內(nèi)容——Serialization。
Serialization V.S. Encoding
Serialization可以看成是把包含相同內(nèi)容的數(shù)據(jù)從一種結(jié)構(gòu) (.NET Object) 轉(zhuǎn)換成另一種結(jié)構(gòu) (XML) 。要實現(xiàn)在兩種不同結(jié)構(gòu)之間的轉(zhuǎn)化,這兩種結(jié)構(gòu)之間必須存在一種Mapping。Serialization的是實現(xiàn)由序列化器(Serializer)來負(fù)責(zé)。而Serializer則利用某種算法(Arithmetic)來提供這種Mapping。我們知道對于一個Managed Type的結(jié)構(gòu)信息——比如它的所有成員的列表,每個成員的Type、訪問限制,以及定在每個成員上的屬性,作為原數(shù)據(jù)被存貯在Assembly的原數(shù)據(jù)表中,這些原數(shù)據(jù)可以通過反射的機制獲得。而XML的結(jié)構(gòu)一般利用XSD來定義。所以 在WCF中的Serialization可以看成是Serializer通過反射的機制分析對象所對應(yīng)的Type的原數(shù)據(jù),從而提供一種算法實現(xiàn)Managed Type的XSD的轉(zhuǎn)化。
很多剛剛接觸WCF的人往往不能很好地區(qū)分Serialization和Encoding。我們的.NET Object通過Serialization轉(zhuǎn)化成XML Infoset。但是要使我們的數(shù)據(jù)能夠通過網(wǎng)絡(luò)協(xié)議在網(wǎng)絡(luò)上傳遞,必須把生成的XML Infoset轉(zhuǎn)化成字節(jié)流(Byte Stream)。所以Encoding關(guān)注的是XML Infoset到字節(jié)流(Byte Stream)這一段轉(zhuǎn)化的過程。在WCF中,有3中不同的方式可供選擇:Binary;Text和MTOM(Message Transmit Optimized Mechanism)。Binary具有最好的Performance,Text具有最好的互操作性,MTOM則有利于大量數(shù)據(jù)的傳送。
我們可以這樣來理解Serialization和Encoding,Sterilization是基于Service Contract的——而實際上它也是定義在Service Contract中,是放在我們的Code中;而Encoding一般由Binding提供,它是和Service無關(guān)的,我們一般在Configuration中根據(jù)實際的需要選擇我們合適的Encoding。WCF把Serialization和Encoding相互分離是有好處的,Serialization手部署環(huán)境的影響相對不大,具有相對的通用性,而Encoding則關(guān)系到訪問Service的性能以及互操作性等方面,和部署環(huán)境緊密相關(guān)。比如對于一個在一個Intranet內(nèi)部使用的系統(tǒng),往往處于提高Performance考慮,我們一般是使用TCP Transport結(jié)合Binary,可能在某一天出現(xiàn)的來自于Internet的潛在的調(diào)用,我們不得不改用Http作為Transport,并使用Text Encoding。由于Encoding是可配置的,所以在這種情況下,我們只需要改變Configuration文件就可以了。
DataContractSerializer
Serialization 是通過Serializer來完成的,在WCF中,我們有3種不同的Serializer——DataContractSerializer(定義在System.RunTime.Serializtion namespace中)、XMLSerializer(定義在System.XML.Serialization namespace)和NetDataContractSerializer (定義在System.XML.Serialization namespace) 。他們不同的方式實現(xiàn).NET Object的Serialization。由于DataContractSerializer和NetDataContractSerializer基本上沒有太大的區(qū)別,我們只討論DataContractSerializer和XMLSerializer。其中DataContractSerializer為WCF默認(rèn)的Serializer,如果沒有顯式定采用另外一種Serializer,WCF會創(chuàng)建一個DataContractSerializer 序列化NET Object。首先我們來討論DataContractSerializer采用怎樣的一種Mapping方式來把.NET Object轉(zhuǎn)化成XML。我們照例用實驗來說明問題。
我們創(chuàng)建兩個類DataContractProduct和DataContractOrder用于表示產(chǎn)品和訂單兩個實體,讀者完全可以命知道描述的內(nèi)容,這里不作特別的介紹。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Artech.WCFSerialization
{
[DataContract]
public class DataContractProduct
{
Private Fields#region Private Fields
private Guid _productID;
private string _productName;
private string _producingArea;
private double _unitPrice;
#endregion
Constructors#region Constructors
public DataContractProduct()
{
Console.WriteLine("The constructor of DataContractProduct has been invocated!");
}
public DataContractProduct(Guid id, string name, string producingArea, double price)
{
this._productID = id;
this._productName = name;
this._producingArea = producingArea;
this._unitPrice = price;
}
#endregion
Properties#region Properties
[DataMember]
public Guid ProductID
{
get { return _productID; }
set { _productID = value; }
}
[DataMember]
public string ProductName
{
get { return _productName; }
set { _productName = value; }
}
[DataMember]
private string ProducingArea
{
get { return _producingArea; }
set { _producingArea = value; }
}
[DataMember]
public double UnitPrice
{
get { return _unitPrice; }
set { _unitPrice = value; }
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Artech.WCFSerialization
{
[DataContract]
[KnownType(typeof(DataContractOrder))]
public class DataContractOrder
{
private Guid _orderID;
private DateTime _orderDate;
private DataContractProduct _product;
private int _quantity;
Constructors#region Constructors
public DataContractOrder()
{
this._orderID = new Guid();
this._orderDate = DateTime.MinValue;
this._quantity = int.MinValue;
Console.WriteLine("The constructor of DataContractOrder has been invocated!");
}
public DataContractOrder(Guid id, DateTime date, DataContractProduct product, int quantity)
{
this._orderID = id;
this._orderDate = date;
this._product = product;
this._quantity = quantity;
}
#endregion
Properties#region Properties
[DataMember]
public Guid OrderID
{
get { return _orderID; }
set { _orderID = value; }
}
[DataMember]
public DateTime OrderDate
{
get { return _orderDate; }
set { _orderDate = value; }
}
[DataMember]
public DataContractProduct Product
{
get { return _product; }
set { _product = value; }
}
[DataMember]
public int Quantity
{
get { return _quantity; }
set { _quantity = value; }
}
#endregion
public override string ToString()
{
return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
this._orderID, this._orderDate, this._product.ProductID, this._product.ProductName, this._product.ProducingArea, this._product.UnitPrice, this._quantity);
}
}
} 使用DataContractSerializer序列化.NET Object。相關(guān)的Type必須運用System.Runtime.Serialization. DataContractAttribute, 需要序列化的成員必須運用System.Runtime.Serialization. DataMemberAttribute。為了使我們能夠了解DataContract默認(rèn)的Mapping機制,我們暫時不在DataContractAttribute和DataMemberAttribute設(shè)置任何參數(shù)。下面我們 來編寫具體的Serialization的代碼:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.Diagnostics;
namespace Artech.WCFSerialization
{
class Program
{
static string _basePath = @"E:\Projects\Artech.WCFSerialization\Artech.WCFSerialization\";
static void Main(string[] args)
{
SerializeViaDataContractSerializer();
}
static void SerializeViaDataContractSerializer()
{
DataContractProduct product = new DataContractProduct(Guid.NewGuid(), "Dell PC", "Xiamen FuJian", 4500);
DataContractOrder order = new DataContractOrder(Guid.NewGuid(), DateTime.Today, product, 300);
string fileName = _basePath + "Order.DataContractSerializer.xml";
using (FileStream fs = new FileStream(fileName, FileMode.Create))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(DataContractOrder));
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs))
{
serializer.WriteObject(writer, order);
}
}
Process.Start(fileName);
}
}
} 代碼很簡單,這里不作特別介紹。我們現(xiàn)在只關(guān)心生成XML是怎樣的結(jié)構(gòu):
這里我們總結(jié)出以下的Mapping關(guān)系:
1. Root Element為對象的Type Name——DataContractOrder
2. Type的Namespace會被加到XML根節(jié)點的Namespace中http://schemas.datacontract.org/2004/07/Artech.WCFSerialization
3. 對象的所有成員以XML Element的形式而不是以XML Attribute的形式輸出。
4. 所以對象在XML的輸出順序是按照字母排序。
5. 所有成員的Elelement 名稱為成員名稱。
6. 不論成員設(shè)置怎樣的作用域(public,protected,internal,甚至市Private),
所有運用了DataMemberAttribute的成員均被序列化到XML中——private string ProducingArea。
7. Type和成員必須運用DataContractAttribute和DataMemberAttribute才能被序列化。
上面這些都是默認(rèn)的Mapping關(guān)系,在通常情況下我們用默認(rèn)的這種Mapping往往不能滿足我們的需求,為了把.NET序列化成我們需要的XML 結(jié)構(gòu)(比如我們的XmL必須于我們預(yù)先定義的XSD一致),我們可以在這兩個Attribute(DataContractAttribute和DataMemberAttribute)制定相關(guān)的參數(shù)來實現(xiàn)。具體做法如下。
1. Root Element可以通過DataContractAttribute中的Name參數(shù)定義。
2. Namespace可以通過DataContractAttribute中的NameSpace參數(shù)定義。
3. 對象的成員只能以XML Element的形式被序列化。
4. 對象成員對應(yīng)的XML Element在XML出現(xiàn)的位置可以通過DataMemberAttribute的Order參數(shù)來定義。
5. 對象成員對應(yīng)的Element的名稱可以通過DataMemberAttribute中的Name定義。
6. 如果不希望某個成員輸出到XML中,可以去掉成員對應(yīng)的DataMemberAttribute Attribute。
此外DataMemberAttribute還有連個額外的參數(shù):
1. IsRequired:制定該成員為必須的,如果通過工具生成XSD的話,對應(yīng)的Element的minOccur=“1”
2. EmitDefaultValue:制定是否輸入沒有賦值的成員(值為默認(rèn)值)是否出現(xiàn)在XML中。
基于上面這些,現(xiàn)在我們修改我們的DataContractOrder和DataContractProduct。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Artech.WCFSerialization
{
[DataContract(Name = "product", Namespace = "http://Artech.WCFSerialization/Samples/Product")]
public class DataContractProduct
{
Private Fields#region Private Fields
private Guid _productID;
private string _productName;
private string _producingArea;
private double _unitPrice;
#endregion
Constructors#region Constructors
public DataContractProduct()
{
Console.WriteLine("The constructor of DataContractProduct has been invocated!");
}
public DataContractProduct(Guid id, string name, string producingArea, double price)
{
this._productID = id;
this._productName = name;
this._producingArea = producingArea;
this._unitPrice = price;
}
#endregion
Properties#region Properties
[DataMember(Name ="id",Order = 1)]
public Guid ProductID
{
get { return _productID; }
set { _productID = value; }
}
[DataMember(Name = "name", Order = 2)]
public string ProductName
{
get { return _productName; }
set { _productName = value; }
}
[DataMember(Name = "producingArea", Order = 3)]
internal string ProducingArea
{
get { return _producingArea; }
set { _producingArea = value; }
}
[DataMember(Name = "price", Order = 4)]
public double UnitPrice
{
get { return _unitPrice; }
set { _unitPrice = value; }
}
#endregion
}
} using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Artech.WCFSerialization
{
[DataContract(Name ="order",Namespace="http://Artech.WCFSerialization/Samples/Order")]
public class DataContractOrder
{
private Guid _orderID;
private DateTime _orderDate;
private DataContractProduct _product;
private int _quantity;
Constructors#region Constructors
public DataContractOrder()
{
this._orderID = new Guid();
this._orderDate = DateTime.MinValue;
this._quantity = int.MinValue;
Console.WriteLine("The constructor of DataContractOrder has been invocated!");
} [KnownType(typeof(DataContractOrder))]
public DataContractOrder(Guid id, DateTime date, DataContractProduct product, int quantity)
{
this._orderID = id;
this._orderDate = date;
this._product = product;
this._quantity = quantity;
}
#endregion
Properties#region Properties
[DataMember(Name ="id",Order =1)]
public Guid OrderID
{
get { return _orderID; }
set { _orderID = value; }
}
[DataMember(Name = "date", Order = 2)]
public DateTime OrderDate
{
get { return _orderDate; }
set { _orderDate = value; }
}
[DataMember(Name = "product", Order = 3)]
public DataContractProduct Product
{
get { return _product; }
set { _product = value; }
}
[DataMember(Name = "quantity", Order = 4)]
public int Quantity
{
get { return _quantity; }
set { _quantity = value; }
}
#endregion
public override string ToString()
{
return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
this._orderID, this._orderDate, this._product.ProductID, this._product.ProductName, this._product.ProducingArea, this._product.UnitPrice, this._quantity);
}
}
}
再次進行序列化,看看得到的XML是什么樣子?
<order xmlns="http://Artech.WCFSerialization/Samples/Order" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<id>994b42c4-7767-4ed4-bdf8-033e99c00a64</id>
<date>2007-03-09T00:00:00+08:00</date>
<product xmlns:a="http://Artech.WCFSerialization/Samples/Product">
<a:id>137e6c34-3758-413e-8f8a-83f26f78a174</a:id>
<a:name>Dell PC</a:name>
<a:producingArea>Xiamen FuJian</a:producingArea>
<a:price>4500</a:price>
</product>
<quantity>300</quantity>
</order> 注:對于DataContract Serializer,這里有兩點需要我們注意的:
1. 由于Serialization是對數(shù)據(jù)的不同結(jié)構(gòu)或形態(tài)的轉(zhuǎn)化,在轉(zhuǎn)化過程中必須預(yù)先知道兩種數(shù)據(jù)相關(guān)的原數(shù)據(jù)(Metadata)。而對于每個.NET對象來說,它的數(shù)據(jù)結(jié)果存放在他所對應(yīng)的Assembly的原數(shù)據(jù)表中(Metadata Table),這些原數(shù)據(jù)表定義的每個定義在該Assembly中的Type的成員定義——包括成員的Type,訪問作用域,成員名稱,以及運用在成員上的所有Attribute。原則上,只要知道.NET對象對應(yīng)的Type,我們就可以通過反射(Reflection)的機制分析出該對象的結(jié)構(gòu)。在該例子中,Serializer要序列化DataContractOrder的對象,必須首先知道該對象所屬的Type——這個Type通過構(gòu)造函數(shù)傳遞給Serializer。但是DataContractOrder定義了一個特殊的成員Product,他屬于我們的自定義Type:DataContractProduct(這里需要特別指出的是對于.NET的基元類型——Primary Type和一半的常用Type——比如DateTime,Guid等等,Serializer可以自動識別,所以不用特別指定),Serializer是不會識別這個對象的,所以我們需要在定義DataContractOrder的時候運用KnownType Attribute來這個Type。
2. 因為在傳統(tǒng)的分布式應(yīng)用中,我們廣泛地采用Serializable Attribute來表明該對象是可以序列化的,DataContract Serializer對這種機制也是支持的。
上面我們講了Serialization,它的本質(zhì)就是把.NETObject轉(zhuǎn)化具有一定結(jié)構(gòu)的XML Infoset。被序列化的成的XML Infoset記過Encoding被進一步轉(zhuǎn)化成適合在網(wǎng)絡(luò)上傳遞的字節(jié)流。當(dāng)這些字節(jié)流從一個Application傳遞到另一個Application,由于我們的程序的業(yè)務(wù)邏輯處理的是一個個的.NET對象,所以在目標(biāo)Application, 會以一個相反的過程把接收到的字節(jié)流重構(gòu)成為和原來一樣的.NET Object——目標(biāo)Application對接收到的字節(jié)流記過Decoding轉(zhuǎn)化成XML Infoset,然后通過創(chuàng)建和用于序列化過程一致的Serializer通過Deserialization重建一個.NET Object。所以這就對Serializer提出了要求——它必須為Managed Type的結(jié)構(gòu)和XML的結(jié)構(gòu)提供可逆性的保證——我們把一個.NET Object序列化成一組XML,然后對這組XML進行反序列化重建的對象必須和原來一致。
現(xiàn)在我們在驗證這種一致性。在上面的Sample中,我們創(chuàng)建了一個DataContractOrder對象,對它進行序列化并把生成的XML保存的一個文件里面(Order.DataContractSerializer.xml),現(xiàn)在我們都讀取這個文件的內(nèi)容,把它反序列化成DataContractOrder 對象,看它的內(nèi)容是否和原來一樣。下面是Deserialization的邏輯。
static void DeserializeViaDataContractSerializer()
{
string fileName = _basePath + "Order.DataContractSerializer.xml";
DataContractOrder order;
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(DataContractOrder));
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()))
{
order = serializer.ReadObject(reader) as DataContractOrder;
}
}
Console.WriteLine(order);
Console.Read();
} 調(diào)用這個方法,通過在控制臺輸出DataContractOrder的內(nèi)容,我們可以確定,通過Deserialization生成的DataContractOrder 對象和原來的對象具有一樣的內(nèi)容。
XMLSerializer提到XMLSerializer,我想絕大多數(shù)人都知道這是asmx采用的Serializer。首先我們還是來看一個例子,通過比較Managed Type的結(jié)構(gòu)和生成的XML的結(jié)構(gòu)來總結(jié)這種序列化方式采用的是怎樣的一種Mapping方式。和DataContractSerialzer Sample一樣,我們要定義用于序列化對象所屬的Type——XMLOrder和XMLProduct,他們和相面對應(yīng)的DataContractOrder和DataContractProduct具有相同的成員。
using System;
using System.Collections.Generic;
using System.Text;
360docimg_501_
360docimg_502_namespace Artech.WCFSerialization
360docimg_503_360docimg_504_360docimg_505_{
360docimg_506_ public class XMLProduct
360docimg_507_360docimg_508_ 360docimg_509_{
360docimg_510_360docimg_511_ Private Fields#region Private Fields
360docimg_512_ private Guid _productID;
360docimg_513_ private string _productName;
360docimg_514_ private string _producingArea;
360docimg_515_ private double _unitPrice;
360docimg_516_
360docimg_517_360docimg_518_ Constructors#region Constructors
360docimg_519_ public XMLProduct()
360docimg_520_360docimg_521_ 360docimg_522_{
360docimg_523_ Console.WriteLine("The constructor of XMLProduct has been invocated!");
360docimg_524_ }
360docimg_525_
360docimg_526_ public XMLProduct(Guid id, string name, string producingArea, double price)
360docimg_527_360docimg_528_ 360docimg_529_{
360docimg_530_ this._productID = id;
360docimg_531_ this._productName = name;
360docimg_532_ this._producingArea = producingArea;
360docimg_533_ this._unitPrice = price;
360docimg_534_ }
360docimg_535_
360docimg_536_ #endregion
360docimg_537_
360docimg_538_360docimg_539_ Properties#region Properties
360docimg_540_ public Guid ProductID
360docimg_541_360docimg_542_ 360docimg_543_{
360docimg_544_360docimg_545_ get 360docimg_546_{ return _productID; }
360docimg_547_360docimg_548_ set 360docimg_549_{ _productID = value; }
360docimg_550_ }
360docimg_551_
360docimg_552_ public string ProductName
360docimg_553_360docimg_554_ 360docimg_555_{
360docimg_556_360docimg_557_ get 360docimg_558_{ return _productName; }
360docimg_559_360docimg_560_ set 360docimg_561_{ _productName = value; }
360docimg_562_ }
360docimg_563_
360docimg_564_ internal string ProducingArea
360docimg_565_360docimg_566_ 360docimg_567_{
360docimg_568_360docimg_569_ get 360docimg_570_{ return _producingArea; }
360docimg_571_360docimg_572_ set 360docimg_573_{ _producingArea = value; }
360docimg_574_ }
360docimg_575_
360docimg_576_ public double UnitPrice
360docimg_577_360docimg_578_ 360docimg_579_{
360docimg_580_360docimg_581_ get 360docimg_582_{ return _unitPrice; }
360docimg_583_360docimg_584_ set 360docimg_585_{ _unitPrice = value; }
360docimg_586_ }
360docimg_587_
360docimg_588_ #endregion
360docimg_589_
360docimg_590_ }
360docimg_591_}
360docimg_592_ 360docimg_593_using System;
360docimg_594_using System.Collections.Generic;
360docimg_595_using System.Text;
360docimg_596_
360docimg_597_namespace Artech.WCFSerialization
360docimg_598_360docimg_599_360docimg_600_{
360docimg_601_ public class XMLOrder
360docimg_602_360docimg_603_ 360docimg_604_{
360docimg_605_ private Guid _orderID;
360docimg_606_ private DateTime _orderDate;
360docimg_607_ private XMLProduct _product;
360docimg_608_ private int _quantity;
360docimg_609_
360docimg_610_360docimg_611_ Constructors#region Constructors
360docimg_612_ public XMLOrder()
360docimg_613_360docimg_614_ 360docimg_615_{
360docimg_616_ this._orderID = new Guid();
360docimg_617_ this._orderDate = DateTime.MinValue;
360docimg_618_ this._quantity = int.MinValue;
360docimg_619_
360docimg_620_ Console.WriteLine("The constructor of XMLOrder has been invocated!");
360docimg_621_ }
360docimg_622_
360docimg_623_ public XMLOrder(Guid id, DateTime date, XMLProduct product, int quantity)
360docimg_624_360docimg_625_ 360docimg_626_{
360docimg_627_ this._orderID = id;
360docimg_628_ this._orderDate = date;
360docimg_629_ this._product = product;
360docimg_630_ this._quantity = quantity;
360docimg_631_ }
360docimg_632_ #endregion
360docimg_633_
360docimg_634_360docimg_635_ Properties#region Properties
360docimg_636_ public Guid OrderID
360docimg_637_360docimg_638_ 360docimg_639_{
360docimg_640_360docimg_641_ get 360docimg_642_{ return _orderID; }
360docimg_643_360docimg_644_ set 360docimg_645_{ _orderID = value; }
360docimg_646_ }
360docimg_647_
360docimg_648_ public DateTime OrderDate
360docimg_649_360docimg_650_ 360docimg_651_{
360docimg_652_360docimg_653_ get 360docimg_654_{ return _orderDate; }
360docimg_655_360docimg_656_ set 360docimg_657_{ _orderDate = value; }
360docimg_658_ }
360docimg_659_
360docimg_660_ public XMLProduct Product
360docimg_661_360docimg_662_ 360docimg_663_{
360docimg_664_360docimg_665_ get 360docimg_666_{ return _product; }
360docimg_667_360docimg_668_ set 360docimg_669_{ _product = value; }
360docimg_670_ }
360docimg_671_
360docimg_672_ public int Quantity
360docimg_673_360docimg_674_ 360docimg_675_{
360docimg_676_360docimg_677_ get 360docimg_678_{ return _quantity; }
360docimg_679_360docimg_680_ set 360docimg_681_{ _quantity = value; }
360docimg_682_ }
360docimg_683_ #endregion
360docimg_684_
360docimg_685_ public override string ToString()
360docimg_686_360docimg_687_ 360docimg_688_{
360docimg_689_ return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
360docimg_690_ this._orderID,this._orderDate,this._product.ProductID,this._product.ProductName,this._product.ProducingArea,this._product.UnitPrice,this._quantity);
360docimg_691_ }
360docimg_692_ }
360docimg_693_}
360docimg_694_
編寫Serialization的Code.
360docimg_695_360docimg_696_
360docimg_697_static void SerializeViaXMLSerializer()
360docimg_698_360docimg_699_ 360docimg_700_{
360docimg_701_ XMLProduct product = new XMLProduct(Guid.NewGuid(), "Dell PC", "Xiamen FuJian", 4500);
360docimg_702_ XMLOrder order = new XMLOrder(Guid.NewGuid(), DateTime.Today, product, 300);
360docimg_703_ string fileName = _basePath + "Order.XmlSerializer.xml";
360docimg_704_ using (FileStream fs = new FileStream(fileName, FileMode.Create))
360docimg_705_360docimg_706_ 360docimg_707_{
360docimg_708_ XmlSerializer serializer = new XmlSerializer(typeof(XMLOrder));
360docimg_709_ using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs))
360docimg_710_360docimg_711_ 360docimg_712_{
360docimg_713_ serializer.Serialize(writer, order);
360docimg_714_ }
360docimg_715_ }
360docimg_716_ Process.Start(fileName);
360docimg_717_ }
360docimg_718_
調(diào)用上面定義的方法,生成序列化的XML。
360docimg_719_<?xml version="1.0" encoding="utf-8"?>
360docimg_720_<XMLOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
360docimg_721_ <OrderID>b695fd18-9cd7-4792-968a-0c0c3a3962c2</OrderID>
360docimg_722_ <OrderDate>2007-03-09T00:00:00+08:00</OrderDate>
360docimg_723_ <Product>
360docimg_724_ <ProductID>23a2fe03-d0a0-4ce5-b213-c7e5196af566</ProductID>
360docimg_725_ <ProductName>Dell PC</ProductName>
360docimg_726_ <UnitPrice>4500</UnitPrice>
360docimg_727_ </Product>
360docimg_728_ <Quantity>300</Quantity>
360docimg_729_</XMLOrder>
360docimg_730_
這里我們總結(jié)出以下的Mapping關(guān)系:
-
Root Element被指定為類名。
-
不會再Root Element中添加相應(yīng)的Namaspace。
-
對象成員以XML Element的形式輸出。
-
對象成員出現(xiàn)的順利和在Type定義的順序一致。
-
只有Public Field和可讀可寫得Proppery才會被序列化到XML中——比如定義在XMLProduct中的internal string ProducingArea沒有出現(xiàn)在XML中。
-
Type定義的時候不需要運用任何Attribute。
以上這些都是默認(rèn)的Mapping關(guān)系,同DataContractSerializer一樣,我們可以通過在Type以及它的成員中運用一些Attribute來改這種默認(rèn)的Mapping。
-
Root Element名稱之后能為類名。
-
可以在Type上運用XMLRoot,通過Namaspace參數(shù)在Root Element指定Namespace。
-
可以通過在類成員上運用XMLElement Attribute和XMLAttribute Attribute指定對象成員轉(zhuǎn)化成XMLElement還是XMLAttribute。并且可以通過NameSpace參數(shù)定義Namespace。
-
可以在XMLElement或者XMLAttribute Attribute 通過Order參數(shù)指定成員在XML出現(xiàn)的位置。
-
可以通過XmlIgnore attribute阻止對象成員被序列化。
基于上面這些,我們重新定義了XMLProduct和XMLOrder。
360docimg_731_using System;
360docimg_732_using System.Collections.Generic;
360docimg_733_using System.Text;
360docimg_734_using System.Xml.Serialization;
360docimg_735_
360docimg_736_namespace Artech.WCFSerialization
360docimg_737_360docimg_738_360docimg_739_{
360docimg_740_ public class XMLProduct
360docimg_741_360docimg_742_ 360docimg_743_{
360docimg_744_360docimg_745_ Private Fields#region Private Fields
360docimg_746_ private Guid _productID;
360docimg_747_ private string _productName;
360docimg_748_ private string _producingArea;
360docimg_749_ private double _unitPrice;
360docimg_750_ #endregion
360docimg_751_
360docimg_752_360docimg_753_ Constructors#region Constructors
360docimg_754_ public XMLProduct()
360docimg_755_360docimg_756_ 360docimg_757_{
360docimg_758_ Console.WriteLine("The constructor of XMLProduct has been invocated!");
360docimg_759_ }
360docimg_760_
360docimg_761_ public XMLProduct(Guid id, string name, string producingArea, double price)
360docimg_762_360docimg_763_ 360docimg_764_{
360docimg_765_ this._productID = id;
360docimg_766_ this._productName = name;
360docimg_767_ this._producingArea = producingArea;
360docimg_768_ this._unitPrice = price;
360docimg_769_ }
360docimg_770_
360docimg_771_ #endregion
360docimg_772_
360docimg_773_360docimg_774_ Properties#region Properties
360docimg_775_ [XmlAttribute("id")]
360docimg_776_ public Guid ProductID
360docimg_777_360docimg_778_ 360docimg_779_{
360docimg_780_360docimg_781_ get 360docimg_782_{ return _productID; }
360docimg_783_360docimg_784_ set 360docimg_785_{ _productID = value; }
360docimg_786_ }
360docimg_787_
360docimg_788_ [XmlElement("name")]
360docimg_789_ public string ProductName
360docimg_790_360docimg_791_ 360docimg_792_{
360docimg_793_360docimg_794_ get 360docimg_795_{ return _productName; }
360docimg_796_360docimg_797_ set 360docimg_798_{ _productName = value; }
360docimg_799_ }
360docimg_800_ [XmlElement("producingArea")]
360docimg_801_ public string ProducingArea
360docimg_802_360docimg_803_ 360docimg_804_{
360docimg_805_360docimg_806_ get 360docimg_807_{ return _producingArea; }
360docimg_808_360docimg_809_ set 360docimg_810_{ _producingArea = value; }
360docimg_811_ }
360docimg_812_
360docimg_813_ [XmlElement("price")]
360docimg_814_ public double UnitPrice
360docimg_815_360docimg_816_ 360docimg_817_{
360docimg_818_360docimg_819_ get 360docimg_820_{ return _unitPrice; }
360docimg_821_360docimg_822_ set 360docimg_823_{ _unitPrice = value; }
360docimg_824_ }
360docimg_825_
360docimg_826_#endregion
360docimg_827_
360docimg_828_ }
360docimg_829_}
360docimg_830_
360docimg_831_using System;
360docimg_832_using System.Collections.Generic;
360docimg_833_using System.Text;
360docimg_834_using System.Xml.Serialization;
360docimg_835_
360docimg_836_namespace Artech.WCFSerialization
360docimg_837_360docimg_838_360docimg_839_{
360docimg_840_ [XmlRoot(Namespace = "http://artech.wcfSerialization/Samples/Order")]
360docimg_841_ public class XMLOrder
360docimg_842_360docimg_843_ 360docimg_844_{
360docimg_845_ private Guid _orderID;
360docimg_846_ private DateTime _orderDate;
360docimg_847_ private XMLProduct _product;
360docimg_848_ private int _quantity;
360docimg_849_
360docimg_850_360docimg_851_ Constructors#region Constructors
360docimg_852_ public XMLOrder()
360docimg_853_360docimg_854_ 360docimg_855_{
360docimg_856_ this._orderID = new Guid();
360docimg_857_ this._orderDate = DateTime.MinValue;
360docimg_858_ this._quantity = int.MinValue;
360docimg_859_
360docimg_860_ Console.WriteLine("The constructor of XMLOrder has been invocated!");
360docimg_861_ }
360docimg_862_
360docimg_863_ public XMLOrder(Guid id, DateTime date, XMLProduct product, int quantity)
360docimg_864_360docimg_865_ 360docimg_866_{
360docimg_867_ this._orderID = id;
360docimg_868_ this._orderDate = date;
360docimg_869_ this._product = product;
360docimg_870_ this._quantity = quantity;
360docimg_871_ }
360docimg_872_ #endregion
360docimg_873_
360docimg_874_360docimg_875_ Properties#region Properties
360docimg_876_ [XmlAttribute("id")]
360docimg_877_ public Guid OrderID
360docimg_878_360docimg_879_ 360docimg_880_{
360docimg_881_360docimg_882_ get 360docimg_883_{ return _orderID; }
360docimg_884_360docimg_885_ set 360docimg_886_{ _orderID = value; }
360docimg_887_ }
360docimg_888_ [XmlElement(ElementName = "date",Order = 3)]
360docimg_889_ public DateTime OrderDate
360docimg_890_360docimg_891_ 360docimg_892_{
360docimg_893_360docimg_894_ get 360docimg_895_{ return _orderDate; }
360docimg_896_360docimg_897_ set 360docimg_898_{ _orderDate = value; }
360docimg_899_ }
360docimg_900_ [XmlElement(ElementName = "product", Order = 1, Namespace = "Http://Artech.WCFSerialization/Samples/Product")]
360docimg_901_ public XMLProduct Product
360docimg_902_360docimg_903_ 360docimg_904_{
360docimg_905_360docimg_906_ get 360docimg_907_{ return _product; }
360docimg_908_360docimg_909_ set 360docimg_910_{ _product = value; }
360docimg_911_ }
360docimg_912_
360docimg_913_ [XmlElement(ElementName = "quantity", Order = 2)]
360docimg_914_ public int Quantity
360docimg_915_360docimg_916_ 360docimg_917_{
360docimg_918_360docimg_919_ get 360docimg_920_{ return _quantity; }
360docimg_921_360docimg_922_ set 360docimg_923_{ _quantity = value; }
360docimg_924_ }
360docimg_925_ #endregion
360docimg_926_
360docimg_927_ public override string ToString()
360docimg_928_360docimg_929_ 360docimg_930_{
360docimg_931_ return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
360docimg_932_ this._orderID,this._orderDate,this._product.ProductID,this._product.ProductName,this._product.ProducingArea,this._product.UnitPrice,this._quantity);
360docimg_933_ }
360docimg_934_ }
360docimg_935_}
360docimg_936_
重新進行一次Serialization。我們可以得到下面的XML。
360docimg_937_<?xml version="1.0" encoding="utf-8"?>
360docimg_938_<XMLOrder id="9a0bbda4-1743-4398-bc4f-ee216e02695b" xmlns="http://artech.wcfSerialization/Samples/Order" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
360docimg_939_ <product id="4e3aabe5-3a51-4000-9fd8-d821d164572a" xmlns="Http://Artech.WCFSerialization/Samples/Product">
360docimg_940_ <name>Dell PC</name>
360docimg_941_ <producingArea>Xiamen FuJian</producingArea>
360docimg_942_ <price>4500</price>
360docimg_943_ </product>
360docimg_944_ <quantity>300</quantity>
360docimg_945_ <date>2007-03-09T00:00:00+08:00</date>
360docimg_946_</XMLOrder>
360docimg_947_
分析完XMLSerializer的Serialization功能,我們照例來分析它的反向過程—Deserialization。下面的Deserialization的Code。
360docimg_948_360docimg_949_
360docimg_950_static void DeserializeViaXMLSerializer()
360docimg_951_360docimg_952_ 360docimg_953_{
360docimg_954_ string fileName = _basePath + "Order.XmlSerializer.xml";
360docimg_955_ XMLOrder order;
360docimg_956_ using (FileStream fs = new FileStream(fileName, FileMode.Open))
360docimg_957_360docimg_958_ 360docimg_959_{
360docimg_960_ XmlSerializer serializer = new XmlSerializer(typeof(XMLOrder), "http://artech.WCFSerialization/Samples");
360docimg_961_ using (XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()))
360docimg_962_360docimg_963_ 360docimg_964_{
360docimg_965_ order= serializer.Deserialize(reader) as XMLOrder;
360docimg_966_ }
360docimg_967_ }
360docimg_968_
360docimg_969_ Console.WriteLine(order);
360docimg_970_ Console.Read();
360docimg_971_}
360docimg_972_
調(diào)用DeserializeViaXMLSerializer,得到下面的Screen Shot。下面顯示的Order對象的信息和我們利用DataContractSerializaer進行Deserialization是的輸出沒有什么兩樣。不過有趣的是上面多出了兩行額外的輸出:The constructor of XMLProduct has been invocated! The constructor of XMLOrder has been invocated。而這個操作實際上是定義在XMLProduct和XMLOrder的默認(rèn)(無參)構(gòu)造函數(shù)里的。所此我們可以得出這樣的結(jié)論——用XMLSerializer進程Deserialization,會調(diào)用的默認(rèn)(無參)構(gòu)造函數(shù)來初始化對象。
360docimg_973_
DataContractSerializer V.S. XMLSerializer
上面我們分別分析了兩種不同的Serializer,現(xiàn)在我們來簡單總結(jié)一下他們的區(qū)別:
特性 | XMLSerializer | DataContractSerializer |
默認(rèn)Mapping | 所有Public Field和可讀可寫Property | 所有DataMember Filed、Property |
是否需要Attribute | 不需要 | DataContract DataMember或者Serializable |
成員的默認(rèn)次序 | Type中定義的順序 | 字母排序 |
兼容性 | .asmx | Remoting |
Deserialzation | 調(diào)用默認(rèn)構(gòu)造函數(shù) | 不會調(diào)用 |