上篇只是介紹了WCF的概述。具體的設置全是使用默認,這當然不可能滿足我們開發的需要。如果仔細理一理的話,你會發現WCF里面的設置其實不算多的(與SharePoint比較的話)。從這篇開始,我們一點一點來展開學習。這次先提最最常用的Contract。
Contract有人翻譯為:協定,契約。
WCF中有四種contract: 分別是:1.Service Contract. 2.Data Contract. 3. Fault Contract. 4.Message Contract.
還拿上篇中的例子(其實就是有Visual Studio2010替我們默認生成的代碼了)說事:
1.Service Contract共分為兩個部分:定義部分和實現部分。
1 [ServiceContract] 2 public interface IService1 3 { 4 [OperationContract] 5 string GetData(int value); 6 7 [OperationContract] 8 CompositeType GetDataUsingDataContract(CompositeType composite); 9 10 }
這里IService就是一個service contract的定義部分,指定了哪些函數(Operation)向外公開(即:可供Client調用)。被[OperationContract]修飾的函數可以被客戶端(Client)調用。
[OperationContent]是Attribute。關於Attribute不太明白的,請參考:《別弄混了C#.的幾個小概念Attribute,property,field》
1 public class Service1 : IService1 2 { 3 public string GetData(int value) 4 { 5 return string.Format("You entered: {0}", value); 6 } 7 8 public CompositeType GetDataUsingDataContract(CompositeType composite) 9 { 10 if (composite == null) 11 { 12 throw new ArgumentNullException("composite"); 13 } 14 if (composite.BoolValue) 15 { 16 composite.StringValue += "Suffix"; 17 } 18 return composite; 19 } 20 }
Service1實現了IService接口,是Service Contract 的實現部分。VS自動生成的代碼太簡單了,不解釋了。這里為了盡可能簡單地說明問題。具體項目中,你可能需要連接數據庫增、刪、改、查數據,或對數據按n多復雜的業務規則進行數據處理以后,返回給client,等。
[ServiceContract] Attribute 可以有以下Property 的:
CallbackContract | 設置callback的類型:Duplicate指Service Host和Client之間進行雙向通信 |
ConfigurationName | 指定配置文件中某個configuration的名字 |
HasProtectionLevel | 標示是否可以處理安全消息 |
Name | 給contract指定一個名字,在client端可見的名字,默認就是接口名字 |
Namespace | 給消息指定一個命名空間 |
ProtectionLevel | |
SessionMode | 指允許,還是不允許,還是強制session |
[OperationContract] Attribute 可以有以下Property 的:
Action | 對請求設置WS-Addressing 的action |
AsynchPattern | 異步模式 |
HasProtectionLevel | 消息是否加密,簽名 |
IsInitiating | 表明該函數被調用開始時是否要在server上面初始化一個session |
IsOneWay | 表明函數被client調用以后,client是否會等待函數返回 |
IsTerminating | 表明該函數被調用結束時是否要在server上面關閉session |
Name | 設置函數的名字,在client端可見的名字,默認就是函數名字 |
ProtectionLevel | |
ReplyAction | 設置函數返回消息的SOAP action |
1.1 我們修改一下Service的默認命名空間,使他更make scene(有意義).
打開上篇中的解決方案WcfFirstDemo.sln
右鍵WebHost項目下面的文件:Service.svc,如下圖:
點擊連接如圖:
得到如圖效果:
默認Namespace是http://tempuri.org/
微軟官方建議:修改Service的Namespace,使其包含:公司域名+項目名+版本號(如:日期表示版本號)
修改項目:WcfFirstDemoServiceLib下面的IService.cs代碼如下:
1 [ServiceContract(Namespace="http://wwww.cnblogs.com/WCF/2012/07/28")] 2 public interface IService1 3 { 4 [OperationContract] 5 string GetData(int value); 6 7 [OperationContract] 8 CompositeType GetDataUsingDataContract(CompositeType composite); 9 10 }
右鍵項目WcfFirstDemoServiceLib 選擇重新編譯,成功以后,重新用瀏覽器打開service.svc,得到如下圖:
此時已經改變了Service默認的Namespace了,client端需要更新一下,否則運行client端是會報異常的.操作如下圖:
1.2修改Service的Name.默認情況下定義service的接口部分(如本例:IService)的名字就是Service的名字.但有時需要讓client看到的service的名字跟server端看到的的service名字不一樣.
我們先看一下WCFClient項目下面的app.config
修改WcfFirstDemoServiceLib項目下的IService.cs文件
1 [ServiceContract(Namespace="http://wwww.cnblogs.com/WCF/2012/07/28",Name="DemoService")] 2 public interface IService1 3 { 4 [OperationContract(Name="GetAge")] 5 string GetData(int value); 6 7 [OperationContract] 8 CompositeType GetDataUsingDataContract(CompositeType composite); 9 10 }
重新編譯WcfFirstDemoServiceLib項目.更新client端對service的引用.此時你會發現client端app.config文件前后發生了變化:
此時WCFClient所生成的代理類的名字也會變化,你需要修改WCFClient
1 DemoService proxy = null;
1 private void Form1_Load(object sender, EventArgs e) 2 { 3 proxy = new DemoServiceClient("BasicHttpBinding_DemoService"); 4 5 }
1 private void btnGetData_Click(object sender, EventArgs e) 2 { 3 this.tbOutputBox.Text = proxy.GetAge(Convert.ToInt32(this.tbInputbox.Text)); 4 }
注:上面代碼中DemoService 對應server端IService接口;DemoServiceClient對應server端的Service類.
當client端的app.config文件中只包含一個endpoint時,可以直接用proxy=new DemoServiceClient()傳空參數,當然也可以傳這個endpoint的name進去;但是當app.config包含多個endpoint時必須把endpoint的name傳進去才能new出proxy對象.
2.DataContract.
WCF的Server和client之間利用特定格式的Message(消息)進行通信的。但是我們使用的高級語言:不論在Server端還是在client端,我們處理的業務數據都被封裝成了對象。所以想把server端的對象傳送到client(或相反方向)時,我們必須有能力把這些對象轉換成特定格式的message,到另一端接收到message后再把它轉回成對象。C#的基礎類型(如int,string,float,bool等)可以做到這一點。因為一旦確定了Server端或client所用的編程語言,就可以確定這些基礎類型的內存占用情況(例如:一個Server端定義的int變量在C#中占用多大內存空間是已知的,那么client端即便使用的是其他語言也完全可以計算出該變量的內存占用情況)。可是我們自己定義的類型可是五花八門了(比如:server端定義的Person類new出的object到底占用多大內存空間,就算Server端與client端使用同一種語言,Client端是不知道的Person類的對象的內存占用情況的。)
遇到這種請款下,我們怎么辦呢?這時就輪到[DataContract]露面了。
遇到這種請款下,我們怎么辦呢?這時就輪到[DataContract]露面了。
ServiceContract做的工作是指定service向client提供了哪些函數可供調用。DataContract做的工作就是指定在Server端與client端之間可以傳送的數據。[DataContract]的作用就是指定當需要傳送某個類(如:Person類)的對象時,將該對象轉化成為XML.接受方接到XML以后,再按同樣的方式還原成對象。
[DataContract]Attribute 標在class定義上面一行。[DataMember]Attribute標在Property定義上面一行,field不需要Attribute修飾。
1 [DataContract] 2 public class CompositeType 3 { 4 bool boolValue = true; 5 string stringValue = "Hello "; 6 7 [DataMember] 8 public bool BoolValue 9 { 10 get { return boolValue; } 11 set { boolValue = value; } 12 } 13 14 [DataMember] 15 public string StringValue 16 { 17 get { return stringValue; } 18 set { stringValue = value; } 19 } 20 }
[DataContract]Attribute 可以像[ServiceContract]一樣設置Name和Namespace.
[DataMember] Attribute 可以有以下屬性:
EmitDefaultValue | 設置一個默認值 |
IsRequired | 進行序列化/反序列化時該值一定不可為空值 |
Name | Property的名字 |
Order | 設置進行序列化/反序列化的順序 |
2.1修改DataContract的默認namespace ,
1 [DataContract(Namespace = "http://wwww.cnblogs.com/WCF/2012/07/28", Name = "CompositeTypeDemo")] 2 public class CompositeType 3 { 4 bool boolValue = true; 5 string stringValue = "Hello "; 6 7 [DataMember(Name="GetBool")] 8 public bool BoolValue 9 { 10 get { return boolValue; } 11 set { boolValue = value; } 12 } 13 14 [DataMember(Name="GetString")] 15 public string StringValue 16 { 17 get { return stringValue; } 18 set { stringValue = value; } 19 } 20 }
這樣在client端看到的類(class)名,函數名與server看到的就不相同.
3.Fault Contract.
在WCF中處理異常(Exception)的方法有些特殊.我們不能單從server處理exception,需要進一步將Exception從server傳送到client. 在后面我們再單獨討論WCF的Exception處理.
4. Message Contract.
Message Contract與Data Contract都是作用在傳送的對象(object)上面.
不同的是:
datacontract是將object序列化化為xml. 實現object的各個property與xml 文本中各個node(節點)的對應);
messagecontract將對象組裝成message(指定Message的Header,body).實現的是object各個property與消息的各個元素的對應.
具體的深層區別於聯系,以及使用場景,我還不懂,希望有朋友對着塊比較懂的,可以交流一下.如果后面等我弄懂了的話,我再專門寫博文交流.