一、數據契約
1.使用數據協定可以靈活控制哪些成員應該被客戶端識別。
[DataContract]public class Employee{[DataMember]public string Name { get; set; }[DataMember]public int Age { get; set; }[DataMember]public string City { get; set; }}[ServiceContract]public interface IService{[OperationContract]Employee GetAEmployee();}public class MyService : IService{public Employee GetAEmployee(){return new Employee { Name = "小朋友", Age = 32 };}}class Program{static void Main(string[] args){using (ServiceHost host = new ServiceHost(typeof(Service1))){host.AddServiceEndpoint(typeof(IService1), new WSHttpBinding(), "http://127.0.0.1:8888/service1");ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();behavior.HttpGetEnabled = true;behavior.HttpGetUrl = new Uri("http://127.0.0.1:8888/service"); //httpGetUrl客戶端引用的地址 host.Description.Behaviors.Add(behavior);host.Opened += delegate{Console.WriteLine("服務已啟動");Console.ReadKey();};host.Open();}}}
如果把Employee類中的某個成員的DataMember屬性去掉,則在客戶端代理類中就不會生成該屬性
2.數據協定也有隱藏真實身份的作用
[DataContract(Name = "Worker")]public class Employee{[DataMember(Name = "Worker_Name")]public string Name { get; set; }[DataMember(Name = "Worker_Age")]public int Age { get; set; }[DataMember(Name = "Worker_City")]public string City { get; set; }}
對數據庫的備份必須序列化?這樣在客戶端生成的代理類的成員名稱就和服務器端不一樣
3.(已知類型),在一些比較復雜的類型無法反序列化(不能識別類型)的時候,就得考慮使用KnownTypeAttribute來標注可能涉及到的外部類型,但如果遇到像泛型這些較為復雜的類型,就要考慮在帶數據協定的類中添加一個靜態方法,該方法返回Type?的IEnumerable,一般是Type[]就可以了,而在KnownTypeAttribute的構造函數中使用這個方法的名字。
[DataContract][KnownType("GetKnowTypes")]public class Student{[DataMember]public string Name;[DataMember]public string Phone;[DataMember]public AddrInfo Address;[DataMember]public object Scores;static Type[] GetKnowTypes(){return new Type[] { typeof(Dictionary<string,float>) };}}[DataContract]public class AddrInfo{[DataMember]public string Province;[DataMember]public string City;[DataMember]public string DetailAddr;}public class MyService : IService{public Student GetStudentInfo(){Student stu = new Student();AddrInfo info = new AddrInfo();info.Province = "廣東省";info.City = "佛山市";info.DetailAddr = "火星路-300號";stu.Name = "小陳";stu.Phone = "1388888888";stu.Address = info;Dictionary<string, float> m_scores = new Dictionary<string, float>();m_scores.Add("語文", 97f);m_scores.Add("英語", 64.5f);m_scores.Add("數學", 38f);m_scores.Add("歷史", 77.6f);m_scores.Add("地理", 82.3f);stu.Scores = m_scores;return stu;}}static void Main(string[] args){using (ServiceHost host = new ServiceHost(typeof(Service1))){host.AddServiceEndpoint(typeof(IService1), new WSHttpBinding(), "http://127.0.0.1:8888/service1");ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();behavior.HttpGetEnabled = true;behavior.HttpGetUrl = new Uri("http://127.0.0.1:8888/service"); //httpGetUrl客戶端引用的地址 host.Description.Behaviors.Add(behavior);host.Opened += delegate{Console.WriteLine("服務已啟動");Console.ReadKey();};host.Open();}}static void Main(string[] args){WS.ServiceClient cli = new WS.ServiceClient();WS.Student stu = cli.GetStudentInfo();string msg = "學生姓名:{0}\n聯系電話:{1}\n" +"地址信息:-----------\n" +"省份:{2}\n" +"市區:{3}\n" +"詳細地址:{4}";Console.WriteLine(msg, stu.Name, stu.Phone, stu.Address.Province, stu.Address.City, stu.Address.DetailAddr);Console.WriteLine("---------------------------------------");Console.WriteLine("學生成績單:");Dictionary<string, float> scores = stu.Scores as Dictionary<string, float>;foreach (var item in scores){Console.Write("{0}:{1}\n", item.Key, item.Value);}Console.ReadKey();}
使用[ServiceKnownType(typeof(Order))]放在服務契約上,使用[KnownType(typeof(Order))]放在數據契約上
[ServiceContract][ServiceKnownType(typeof(Order))]//可以放到這里public interface IService1{[OperationContract]//[ServiceKnownType(typeof(Order))]也可以放到這里void AddOrder(OrderBase order);}[DataContract]// [KnownType(typeof(Order))]也可以放到這里public abstract class OrderBase{[DataMember]public Guid ID { get; set; }[DataMember]public DateTime Date { get; set; }[DataMember]public string Customer { get; set; }[DataMember]public string ShipAddress { get; set; }}[DataContract]public class Order : OrderBase{[DataMember]public double TotalPrice { get; set; }}public class Service1 : IService1{public void AddOrder(OrderBase order){Console.WriteLine(order.Customer);}}public class Program{static void Main(string[] args){using (Service1Client client = new Service1Client()){Order order = new Order() { Customer = "yxl" };client.AddOrder(order);}Console.ReadKey();}}
如果不標識ServiceKnownType或KnownType,客戶端將不會生成Order類
二、序列化
wcf文斯莫克。1.NET序列化機制
? 在.NET Framework 3.0之前,提供了3中序列化器,序列化器理解為把可序列化的類型序列化成XML的類。這三種序列化器分別是BinaryFormatter、SoapFormatter和XmlSerializer類。下面分別介紹下這3種序列化器。
- BinaryFormatter類:把.NET Object序列化成二進制格式。在這個過程,對象的公共字段和私有字段以及類名稱(包括類的程序集名),將轉換成成字節流。
- SoapFormatter類:把.NET Object序列化成SOAP格式,SOAP是一種輕量、簡單的,基于XML的協議。只序列化字段,包括公共字段和私有字段。
- XmlSerializer類:該類僅僅序列化公共字段和屬性,且不保存類型的保真度。
對于這三種序列化機制,BinaryFormatter二進制序列化的優點是:性能高,但是不能跨平臺。而SoapFormatter,XmlSerializer的優點是:跨平臺、互操作性好,并且可讀性強,但是傳輸性能不及BinaryFormatter。
原有的格式化器需要將類型的程序集及版本控制信息持久化到流中,以保證對象被反序列化為正確的類型,這種方式一定程度上妨礙面向服務交互。原因如下:BinaryFormatter和SoapFormatter具備類型保真,它們要求其所在程序集、版本等信息必須完全一致,如果不同則視為是不同類型的對象,這就要求客戶端必須擁有原有的.NET 程序集。面向服務的交互方式應該更傾向于平臺無關,并最大限度的解耦,因此這2個格式化器顯然與WCF有些不搭。再看XmlSerializer格式化器,在功能上與DataContractSerializer格式器類似,但是為何沒有使用XmlSerializer格式器而是引人新格式器,我的理解如下:在SOA服務的構建過程中,提倡契約優先的構建原則,也就是說我們應該優先構建契約后構建具體的代碼,在構建過程中并不考慮其具體的序列化過程。(契約優先相對于代碼優先在設計層面具有更高的抽象程度,降低了服務設計與平臺的耦合度,因此我更傾向于契約優先的開發順序。)此外,XmlSerializer為如何將數據表示成XML提供了精確的控制,DataContractSerializer則提供了較少的控制,所以DataContractSerializer的序列化過程是可預知的,容易優化,所以它與XmlSerializer相比有更好的性能(我查到的數字是提升了約10%)。
2.WCF中序列化機制
序列化數據的安全性。在WCF中,提供了專門用來序列化和反序列操作的類,該類就是DataContractSerializer類。一般而言,WCF會自動選擇使用DataContractSerializer來對可序列話數據契約進行序列化,不需要開發者直接調用。WCF除了支持DataContractSerializer類來進行序列化外,還支持另外兩種序列化器,這兩種序列化器分別為:XMLSerializer(定義在System.XML.Serialization namespace)和NetDataContractSerializer?(定義在System.XML.Serialization namespace)。XmlSerializer類不是WCF專用的類,Asp.net Web服務統一使用該類作為序列化器,但XmlSerializer類支持的類少于DataContractSerializer列支持的類型,但它允許對生成的XML進行更多的控制,并且支持更多的XML架構定義語言(XSD)標準。它不需要在可序列化類上有任何聲明性的屬性。
默認情況下,WCF 使用?DataContractSerializer?類來序列化數據類型。?此序列化程序支持下列類型:
- 基元類型(如:整數、字符串和字節數組)以及某些特殊類型(如?XmlElement?和?DateTime)。
- 數據協定類型(用?DataContractAttribute?屬性標記的類型)。
- 用?SerializableAttribute?屬性標記的類型,包括實現?ISerializable?接口的類型。
- 實現?IXmlSerializable?接口的類型。
- 許多常見集合類型,包括許多泛型集合類型。
DataContractSerializer類與NetDataContractSerializer類類似,它們之間主要的區別在于:在使用NetDataContractSerializer進行序列化時,不需要指定序列化的類型,如:
NetDataContractSerializer serializer =new NetDataContractSerializer(); // 不需要明確指定序列化的類型 serializer.WriteObject(writer, p);// 而使用DataContractSerializer需要明確指定序列化的類型 DataContractSerializer serializer =new DataContractSerializer(typeof(Order)); // 需要明確指定序列化的類型serializer.WriteObject(writer, p);
SerializableAttribute與DataContract異同。
相同點:都是標記類型為可序列化類型
wcf框架、不同點:在于序列化的成員不一樣,DataContract是Opt-in(明確參與)的方式,即使用DataMember特性明確標識哪些成員需要序列化,而Serializable是Opt-out方式,即使用NoSerializable特性明確標識不參與序列化的成員。
我個人意見,DataContractSerializable就用在它應該用的地方吧,如果不是用WCF,還是不要用它了,它的序列化結果有一些微軟專屬的東西。對于來自網絡的松散Xml接口數據,XmlSerializer是不二之選。如果想把對象完整地保存下來(數據與狀態),同時又不需要被人看。那就用BinaryFormatter吧,SerializableAttribute默認是使用BinaryFormatter序列化的。