WCF能夠托管CLR類型,客戶端能傳遞和處理CLR類型的數據(如:string和int),但是如果我們自己定義的類型(如:聲明的Customer類)。其實WCF的傳遞CLR自己類型時,是因為已經把它序列化成了xml信息集,而我們自己定義的類型則需要自己去顯示的聲明序列化。
序列化
.net的序列化。.net是通過反射機制(反射機制參考文檔)自動實現對象的序列化與反序列化。首先.net能夠捕獲對象每個字段的值,然後進行序列化,反序列化時,.net創建一個對應類型的新的對象,讀取持久化的值,然後設置字段的值。.net對象狀態的序列化到Stream中。
我們自定義的類或者結構是不支持序列化的,因此如果我們的類是否要序列化是需要類的開發者來指定的。如果需要支持序列化的話,我們可以在類或者結構上添加Serializable屬性。
如:
[Serializable] public class Customer { ... }
但是如果添加了Serializable就要求類型的成員也要支持序列化的,不然會拋出異常,但是我們可以通過在成員上添加NonSerizlized屬性來說明這個成員不需要序列化。
格式器
.net為序列化和反序列化提供了兩種格式器:
1.BinaruFormatter:將類型序列化為二進制格式。
2.SoapFormatter:格式成SOAP特定的XML格式。
但是這兩者都要將類型的程序集以及版本控制信息持久化到Stream中,而面向服務要求參與者都能擁有類型程序集,所以如果用這兩種格式就必須要去共享Stream,這樣肯定不是我們理想的。從而有了WCF格式器。
WCF的格式器:DataContractSerializer,是能夠共享數據契約的。WCF數據進行序列化時,WCF一般會自己調用DataContractSerializer來序列的,不需要我們去指定的。同時我們也可以用DataContractSerializer進行Stream的序列化。
數據契約特性
Serializable要求類型的所有的成員都是可以序列化的,而且不支持類型名跟成員名的別名,這樣我們就沒法明確指定哪些成員是要放到數據契約中的。然而WCF數據契約特性DataContract很好的解決了這些問題。
DataContract Attribute 是在命名空間System.Runtime.Serialization中的,是可以在Enum、Struct、Class上標識的。擁有成員Name,Namespace。而當我們在一個class或者struct上使用時,wcf是不會序列化類型的成員的。如:
[DataContract] public class Emloyee { public string _firstName; public string _lastName; }
Employee裡面的成員_firstName,_lastName是不會去序列化的,所以我們還必須去顯示的指定需要序列化的成員。
DataMember Attribute是用來指定需要序列化類型的成員。DataMember的成員:
IsRequried:序列化時是否必須要賦值;
Name:指定成員的別名;
Order:指定序列化後的排列位置。
我們看一個例子Customer.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Runtime.Serialization; namespace HenllyeeDataContract { [DataContract(Name="Customer",Namespace="HenllyeeDataContract")] public class Customer { Fileds#region Fileds private string _firstName; private string _lastName; private int _age; private decimal _salary; #endregion Attributes#region Attributes [DataMember(Name="FirstName",IsRequired=false,Order=0)] public string FirstName { get { return this._firstName; } set { this._firstName = value; } } [DataMember(Name = "LastName", IsRequired = false, Order = 1)] public string LastName { get { return this._lastName; } set { this._lastName = value; } } [DataMember(Name = "Age", IsRequired = false, Order = 2)] public int Age { get { return this._age; } set { this._age = value; } } [DataMember(Name = "Salary", IsRequired = false, Order = 3)] public decimal Salary { get { return this._salary; } set { this._salary = value; } } #endregion } }
上面我們定義了一個數據契約,將DataMember屬性定義在幾個屬性上(建議定義在屬性上,不要定義在字段上)。
服務契約為:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace HenllyeeServiceContract { [ServiceContract(Name="CustomerManagerService",Namespace="http://Henllyee.cnblogs.com/2008/06/29")] public interface ICustomerManager { /**//// <summary> /// Save customer's info /// </summary> /// <param name="customer">the customer's object</param> [OperationContract(Name="SaveCustomerInfo",Action="http://Henllyee.cnblogs.com/2008/06/29/CustomerManagerService/SaveCustomer", ReplyAction = "http://Henllyee.cnblogs.com/2008/06/29/CustomerManagerService/SaveCustomerResponse")] void SaveCustomer(HenllyeeDataContract.Customer customer); /**//// <summary> /// Get customer's info /// </summary> /// <returns>a customer</returns> [OperationContract(Name = "GetCustomerInfo", Action = "http://Henllyee.cnblogs.com/2008/06/29/CustomerManagerService/GetCustomer", ReplyAction = "http://Henllyee.cnblogs.com/2008/06/29/CustomerManagerService/GetCustomerResponse")] HenllyeeDataContract.Customer GetCustomer(); } }
服務契約裡面具體實現:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace HenllyeeServiceContract { [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class CustomerManager:ICustomerManager { fileds#region fileds private HenllyeeDataContract.Customer _customer; #endregion Members#region Members public void SaveCustomer(HenllyeeDataContract.Customer customer) { _customer = customer; } public HenllyeeDataContract.Customer GetCustomer() { return this._customer; } #endregion } }
主機的實現就不在次給出了。
然後我們在客戶端添加服務引用,即可以了。我們在客戶端可以看到導入的數據契約:
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Name="Customer", Namespace="HenllyeeDataContract")] [System.SerializableAttribute()] public partial class Customer : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged { [System.NonSerializedAttribute()] private System.Runtime.Serialization.ExtensionDataObject extensionDataField; [System.Runtime.Serialization.OptionalFieldAttribute()] private string FirstNameField; [System.Runtime.Serialization.OptionalFieldAttribute()] private string LastNameField; [System.Runtime.Serialization.OptionalFieldAttribute()] private int AgeField; [System.Runtime.Serialization.OptionalFieldAttribute()] private decimal SalaryField; [global::System.ComponentModel.BrowsableAttribute(false)] public System.Runtime.Serialization.ExtensionDataObject ExtensionData { get { return this.extensionDataField; } set { this.extensionDataField = value; } } [System.Runtime.Serialization.DataMemberAttribute()] public string FirstName { get { return this.FirstNameField; } set { if ((object.ReferenceEquals(this.FirstNameField, value) != true)) { this.FirstNameField = value; this.RaisePropertyChanged("FirstName"); } } } [System.Runtime.Serialization.DataMemberAttribute()] public string LastName { get { return this.LastNameField; } set { if ((object.ReferenceEquals(this.LastNameField, value) != true)) { this.LastNameField = value; this.RaisePropertyChanged("LastName"); } } } [System.Runtime.Serialization.DataMemberAttribute(Order=2)] public int Age { get { return this.AgeField; } set { if ((this.AgeField.Equals(value) != true)) { this.AgeField = value; this.RaisePropertyChanged("Age"); } } } [System.Runtime.Serialization.DataMemberAttribute(Order=3)] public decimal Salary { get { return this.SalaryField; } set { if ((this.SalaryField.Equals(value) != true)) { this.SalaryField = value; this.RaisePropertyChanged("Salary"); } } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } }
我們可以看到字段後面都加上了Field後綴。
要注意的一點是:即使我們將DataMember定義在私有的字段或者私有屬性上面,導入的定義中,原來的私有字段或私有屬性都會被重新定義成了公有字段跟公有屬性。