Windows Communication Foundation (WCF) 是基於服務導向架構 (SOA) 的分布式技術。在 SOA 系統中,各子系統自治且鬆耦合,透過共享 Contract(而非傳統型別)進行互動。系統間的通訊本質是訊息交換,Client 發送 SOAP Message 呼叫 Service,Service 執行後回傳結果 Message。WCF 提供完整基礎設施處理這一切:從服務呼叫轉換為 Message Exchange、.NET 物件與 XML Infoset 互轉、XML Infoset 與位元組流編碼/解碼、安全性、可靠傳訊、工作階段與交易支援等。
序列化是將 .NET 物件結構轉換為 XML 結構的過程,兩者之間需有映射關係,由 Serializer 透過反射分析型別中繼資料實現。編碼則是將 XML Infoset 轉換為網路傳輸的位元組流,WCF 提供 Binary、Text、MTOM 三種編碼方式。序列化屬於 Service Contract 範疇(定義於程式碼),編碼屬於 Binding 範疇(通常於設定檔配置),兩者分離使部署更靈活。
DataContractSerializer 是 WCF 預設序列化器。類別需標記 [DataContract],成員需標記 [DataMember] 才會被序列化。以下範例定義 DataContractProduct 與 DataContractOrder:
[DataContract]
public class DataContractProduct
{
[DataMember]
public Guid ProductID { get; set; }
[DataMember]
public string ProductName { get; set; }
[DataMember]
private string ProducingArea { get; set; } // private 也可序列化
[DataMember]
public double UnitPrice { get; set; }
// 建構函式省略
}
[DataContract]
[KnownType(typeof(DataContractOrder))]
public class DataContractOrder
{
[DataMember]
public Guid OrderID { get; set; }
[DataMember]
public DateTime OrderDate { get; set; }
[DataMember]
public DataContractProduct Product { get; set; }
[DataMember]
public int Quantity { get; set; }
// 建構函式與 ToString 省略
}
序列化程式碼:
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);
}
產生的 XML 結構特點:
DataContractOrder。https://schemas.datacontract.org/2004/07/Artech.WCFSerialization)。[DataMember] 即會序列化(含 private 成員)。[DataContract] 與 [DataMember] 才參與序列化。[DataContract] 與 [DataMember] 提供豐富參數調整輸出:
Name:指定元素/型別名稱。Namespace:指定命名空間。Order:控制成員輸出順序。IsRequired:對應 XSD minOccurs="1"。EmitDefaultValue:控制預設值是否輸出。調整後的類別定義:
[DataContract(Name = "product", Namespace = "https://Artech.WCFSerialization/Samples/Product")]
public class DataContractProduct
{
[DataMember(Name = "id", Order = 1)]
public Guid ProductID { get; set; }
[DataMember(Name = "name", Order = 2)]
public string ProductName { get; set; }
[DataMember(Name = "producingArea", Order = 3)]
internal string ProducingArea { get; set; }
[DataMember(Name = "price", Order = 4)]
public double UnitPrice { get; set; }
}
[DataContract(Name = "order", Namespace = "https://Artech.WCFSerialization/Samples/Order")]
public class DataContractOrder
{
[DataMember(Name = "id", Order = 1)]
public Guid OrderID { get; set; }
[DataMember(Name = "date", Order = 2)]
public DateTime OrderDate { get; set; }
[DataMember(Name = "product", Order = 3)]
public DataContractProduct Product { get; set; }
[DataMember(Name = "quantity", Order = 4)]
public int Quantity { get; set; }
// KnownType 屬性標記於建構函式上(範例中位置較特殊)
}
序列化後 XML 變為:
<order xmlns="https://Artech.WCFSerialization/Samples/Order" xmlns:i="https://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="https://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>
序列化時若包含自訂型別(如 DataContractProduct),需在父類別標記 [KnownType] 讓 Serializer 識別。此外,DataContractSerializer 也支援傳統 [Serializable] 屬性標記的型別。
反序列化程式碼:
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();
}
執行結果證明反序列化物件與原始物件內容一致,驗證了序列化/反序列化的可逆性。