前言
昨天早上去醫院做入職體檢,被告知要預約,本以為是要排隊,我連視頻都准備好了。。。結果就回來了。下午去了新公司那邊找房子,2了,因為公司提供了班車列表,我既然就只在班車所經過的幾個地方找,卻遺漏了公司附近這個重要的地址。最后找了一個“江景房”,上陽台就能看到錢塘江。價格和現在的比翻了一倍,累了,不想找了。
有朋友讓我把標題前綴“年前辭職”4個字拿了,好吧,我承認,我就是靠這個吸引一部分眼球的。
第六集 WCF DataContract & DataMember (WCF的Data和DataMember)
這些天寫下來關於那個mex還是有點困惑,早上在stackoverflow上搜到一個回答,感覺寫得挺好的,在此拿出來分享一下。地址:http://stackoverflow.com/questions/21522493/what-was-the-difference-between-wsdl-mex-endpoint-in-wcf 。或許如果你有WebService的經驗,理解起來會更輕松一些。站在使用者的角度,我試着拿掉了endpoint有關mex的定義,以及注釋了behaviors節點,然后訪問http://localhost:8080/ 頁面給了我這么一個提示:
還是回到了最初。
還有一點,stackoverflow回答中向我們傳遞了一個意思,關於WCF,就像他這么用就行,因為,WCF本身還有更多的復雜有趣的東西等我們去開發實踐。
今天講第六集,這兩個是用來修飾需要序列化的實體類的特性,並且也會涉及到KnownType 看了之后覺得是一個很有用的特性。
首先,我們來實現一個EmployeeService,主要的作用是用來根據id查詢Employee,以及向數據庫插入Employee。下面開始介紹:
數據庫部分
我們新建了一張表,叫Employee,左邊是表結構,右邊是內容,Id列沒有用自增。
然后新建了2個存儲過程,一個spGetEmployeeById,一個SaveEmployee
select * from Employee where Id= @id;
insert into Employee (Id ,Name,Gender,DateOfBirth) values (@id ,@name,@gender,@dateOfBirth)
其他定義部分的就寫出來了。
服務部分

1 public class Employee 2 { 3 private int _id; 4 private string _name; 5 private bool _gender; 6 private DateTime _dateOfBirth; 7 8 public int Id 9 { 10 get { return _id; } 11 set { this._id = value; } 12 } 13 public String Name 14 { 15 get { return _name; } 16 set { this._name = value; } 17 } 18 public bool Gender 19 { 20 get { return _gender; } 21 set { this._gender = value; } 22 } 23 public DateTime DateOfBirth 24 { 25 get { return _dateOfBirth; } 26 set { this._dateOfBirth = value; } 27 } 28 }
上面是Employee 類的定義,有人可能會納悶,為什么要額外定義個私有變量,C#這個人性化的語言不是只要寫get;set就ok?開始我也有這個樣的疑惑。但其實,這里是為了說明問題,特地這么寫的。

public class EmployeeService : IEmployeeService { public Employee GetEmployee(int id) { Employee emp = null; var connStr = ConfigurationManager.ConnectionStrings["WCFEmployee"].ConnectionString; using(var conn = new SqlConnection(connStr)) { conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "spGetEmployeeById"; cmd.Parameters.Add(new SqlParameter("id", id)); var reader = cmd.ExecuteReader(); if(!reader.HasRows) return emp; emp = new Employee(); while(reader.Read()) { emp.Id = Convert.ToInt32(reader["Id"]); emp.Name = reader["Name"].ToString(); emp.Gender = Convert.ToBoolean(reader["Gender"]); emp.DateOfBirth = Convert.ToDateTime(reader["DateOfBirth"]); } } return emp; } public void SaveEmployee(Employee emp) { var connStr = ConfigurationManager.ConnectionStrings["WCFEmployee"].ConnectionString; using(var conn = new SqlConnection(connStr)) { conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "spSaveEmployee"; cmd.Parameters.Add(new SqlParameter("id", emp.Id)); cmd.Parameters.Add(new SqlParameter("name", emp.Name)); cmd.Parameters.Add(new SqlParameter("gender", emp.Gender)); cmd.Parameters.Add(new SqlParameter("dateOfBirth", emp.DateOfBirth)); cmd.ExecuteNonQuery(); } } }
上面是EmployeeService.cs 用了最基本的ADO.net操作。
再建一個控制台程序,來托管這個服務,運行成功。
客戶端調用
新建一個WebForm的客戶端,實現如下效果,代碼不貼了,都很基本。
在id框里面輸入id,查詢這個id的對應的信息。
沒有查到給提示。
輸入信息點保存提示,保存到數據庫。
寫完之后,一切運行順利。在介紹下面東西之前,我們先介紹幾個概念
- 什么是Serialization和Deserialization
從WCF角度來說,Serialization(序列化)是個轉換的過程,它把一個實體類轉換為XML,反過來講,通過XML文件,得到一個實體類的過程叫Deserialization(反序列化)。
- 序列化有什么用
這里有一篇 stackoverflow 上的問答,大致意思是說用來存儲以及傳輸數據用的。and 序列化是必須的。
如果不特殊指定,WCF用DataContractSerializer來序列化object(終於出現標題上的關鍵字了)。
為了序列化Employee,我們可以用SerializableAttribute 或者 DataContractAttribute特性修飾這個類。但看看上面的Employee類,我們並沒有加上那兩個特性。那是因為,從framework 3.5開始,如果我們沒有使用DataContract 或者DataMember 特性,那么WCF的DataContractSerializer會自動把所有的public屬性按照字典序的順序序列化。讓我們訪問http://localhost:8080/?wsdl 來看看:
搜索datacontract
然后在地址欄里面輸入后面的schemaLocation的值 http://localhost:8080/?xsd=xsd2 回車:
Employee是一個complexType,下面的sequence里面有他的四個屬性。這是默認生成的schema。
上面說了我們可以通過給一個類加Serializable或者是DataContract特性來顯式標記一個需要序列化的類,下面我們來看看這兩種方式有什么不同。
先看用Serializable標記的:
我們看到,所有帶下划線的私有變量都被序列化了。
再看看用DataContract的效果:
由於我們只給類標記了DataContract特性,沒有任何字段被序列化了。。。(因為沒有序列化字段,客戶端在調用這個類的時候也是無法獲取到對應的屬性的。如圖:
)
其實,DataContract應該是和DataMember配合使用。並且,這也是WCF推薦的做法。下面我們來實現一個。
在此之前,我們先看一下DataMember特性所包含的屬性:鏈接
通過這些屬性,我們可以自由的控制他們在序列化時的名稱,順序等等。

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.Text; 6 7 namespace EmployeeService 8 { 9 [DataContract] 10 public class Employee 11 { 12 private int _id; 13 private string _name; 14 private bool _gender; 15 private DateTime _dateOfBirth; 16 17 [DataMember(Order = 1)] 18 public int Id 19 { 20 get { return _id; } 21 set { this._id = value; } 22 } 23 [DataMember(Order = 2)] 24 public String Name 25 { 26 get { return _name; } 27 set { this._name = value; } 28 } 29 [DataMember(Order = 3)] 30 public bool Gender 31 { 32 get { return _gender; } 33 set { this._gender = value; } 34 } 35 [DataMember(Order = 4)] 36 public DateTime DateOfBirth 37 { 38 get { return _dateOfBirth; } 39 set { this._dateOfBirth = value; } 40 } 41 } 42 }
通過添加DataMember特性,字段回來了,並且序列化的順序也按照我的賦予的排好了。
總結一下,用DataContract 和 DataMember來控制我們需要序列化的對象。
下面還有KnowTypeAttribute的知識點,貌似有不少東西好寫,還是另開一篇吧。。。文章長了看的人就更少了。。。
Thank you!