C#學習之簡單的WCF例子


 

學silverlight學到WCF,嗯,WCF的確有點難,不過沒所謂,一點一點來,下面就嘗試着實現一個最基本最簡單的WCF例子,里面不涉及到EndPoint、Binding、Element、異步調用、序列化等等這些或者其他更難理解的概念,只是簡單地按照默認設置用最簡單的代碼把WCF最基本的應用體現出來。

第一步:創建一個空的解決方案,新建一個WCF服務應用程序項目(使用默認名字) 來模擬服務端,新建一個控制台應用程序項目(名稱改為 ConsoleApp)來模擬客戶端。

  

 

第二步:簡單分析WcfService1項目,該項目內容如下:

一句話總結:這個項目模擬服務器端,Service1.svc文件封裝的就是提供給客戶端的服務引用,Service1.svc.cs文件里是服務引用的具體實現。但這里因為Service1.svc.cs文件里的主要內容——Service1類是繼承於 IService1.cs文件里的 IService1接口,所以重頭戲分了一半給IService1.cs文件。

首先看IService1.cs文件,從名字上可以看得出這個是接口文件,里面定義了了一些接口,接口聲明了一些方法。我在里面添加兩個類 public class student1 、 public class student2 和一個方法 public student1  student1Class() 用作測試。代碼如下:

View Code
 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Runtime.Serialization;
5 using System.ServiceModel;
6 using System.ServiceModel.Web;
7 using System.Text;
8
9 namespace WcfService1
10 {
11 // 注意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼和配置文件中的接口名“IService1”。
12 [ServiceContract]
13 public interface IService1
14 {
15
16 [OperationContract]
17 string GetData(int value);
18
19 [OperationContract]
20 CompositeType GetDataUsingDataContract(CompositeType composite);
21
22 [OperationContract]
23 student1 student1Class();
24
25 // TODO: 在此添加您的服務操作
26 }
27
28 [DataContract]
29 public class student1
30 {
31 string str = "from IService1's student1";
32
33 [DataMember]
34 public string Str { set { str = value; } get { return str; } }
35 }
36
37 [DataContract]
38 public class student2
39 {
40 string str = "from IService2's student1";
41
42 [DataMember]
43 public string Str{ set{str = value;} get{return str;} }
44 }
45
46 // 使用下面示例中說明的數據約定將復合類型添加到服務操作。
47 [DataContract]
48 public class CompositeType
49 {
50 bool boolValue = true;
51 string stringValue = "Hello ";
52
53 [DataMember]
54 public bool BoolValue
55 {
56 get { return boolValue; }
57 set { boolValue = value; }
58 }
59
60 [DataMember]
61 public string StringValue
62 {
63 get { return stringValue; }
64 set { stringValue = value; }
65 }
66 }
67 }

上面這段代碼要注意以下幾方面:

1、服務契約

     接口IService1前面加了 [ServiceContract] ,意思是把這個接口(包括繼承這個接口的類)聲明為服務契約,服務契約是對客戶端而言的,就是這個接口 暴露 在客戶端面前,就是讓客戶端可得見這個接口。但看得見接口不表達可以看得見接口里聲明的方法,這是兩回事(原因很簡單,就算接口是可見的,但里面的方法也有一些是可見另一些不可見的嘛),如果想把方法也聲明為對客戶端可見的,得在聲明方法的簽名加 [OperationContract],這也叫服務契約。總結:服務契約有兩種,[ServiceContract]是聲明接口、類對客戶端可見的,[OperationContract]是具體聲明類里的那些方法對客戶端可見。

2、數據契約

     接口自帶的類CompositeType 和我們自定義的類student1、student2 前面都加了 [DataContract] ,意思是把這個類聲明為數據契約,這樣子客戶端就可以用這個類去定義變量了。這里說的類和上面說的類所指的具體內容稍稍有點不同,上面的類偏向於可以調用類中的方法,這里的類偏向於指作為一種類型(就像int string 等類型),可以用來定義變量。類對客戶端可見,但類中的字段、變量則不一定(原理和上面說的一樣),要在想暴露在客戶端的字段、變量前面加 [DataMember] 。但不能在方法(如構造函數)前面加[DataMember],因為函數只能聲明為服務契約嘛(加[OperationContract]),而服務契約只能在 [ServiceContract] 下面聲明。總結:數據契約有兩種,[DataContract]是聲明類或結構的,[DataMember] 是聲明類或結構中具體的字段或屬性(推薦用屬性)對客戶可見。

 

再來看Service.svc.cs文件。可以看到里面只是定義了一個繼承 IService1接口的類 Service1,主要內容就是實現IService1接口里聲明的方法。代碼如下:

View Code
 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Runtime.Serialization;
5 using System.ServiceModel;
6 using System.ServiceModel.Web;
7 using System.Text;
8
9 namespace WcfService1
10 {
11 // 注意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼、svc 和配置文件中的類名“Service1”。
12
13 public class Service1 : IService1
14 {
15 public string GetData(int value)
16 {
17 return string.Format("You entered: {0}", value);
18 }
19
20 public CompositeType GetDataUsingDataContract(CompositeType composite)
21 {
22 if (composite == null)
23 {
24 throw new ArgumentNullException("composite");
25 }
26 if (composite.BoolValue)
27 {
28 composite.StringValue += "Suffix";
29 }
30 return composite;
31 }
32
33 public student1 student1Class()
34 {
35 return null;
36 }
37
38
39 }
40
41 }

里面的定義的類或者是實現的方法前面都沒有加什么 [ServiceContract] 或 [OperationContract] 聲明,因為類繼承的接口已經聲明了,所以類就不用再聲明[ServiceContract] 了,實現接口的方法 也不用加[OperationContract] 。

可能大家有一個問題,那就是在這個類里添加自己的方法行不?答案是可以,但沒意義。因為在這個類作為服務端(可以這么認為),里面的方法就是為了給客戶端使用的,那么就要在聲明的方法前加[OperationContract],但只有加了 [ServiceContract]屬性的類里的方法才能那樣子,又因為類是繼承了一個具有 [ServiceContract]屬性的接口了的,所以這個類就不用再加 [ServiceContract]屬性了(已經有了),一句話,就是在 繼承了 實現了服務契約的接口 的類里沒必要。添加自己的方法。要加就先加在接口里。

當然,如果你不用接口(可以不用接口的,用接口只是為了更面向對象而已),直接在類里定義服務契約的話,直接把接口里的那些[ServiceContract] 、 [OperationContract] 照搬過來就行了。

 

第三步:分析ConsoleApp項目

 

一句話總結:紅框框里的那個東西叫服務引用,也就是客戶端,也就是靠它才能調用服務端提供的方法和自定義類型。添加方法如下:

右鍵點擊 ConsoleApp項目,選擇 添加服務引用(使用默認名稱),會彈出一方框:

點右邊的 “發現”,就會檢索出發現的服務並顯示在左邊,並把地址也顯示出來。點左邊 服務里發現的服務左邊的三角形,一級級打開,赫然發現里面就是 Service1 類,再往下就是 Service1類繼承的接口 IService1 !對應右邊就是該服務提供給客戶端調用的方法!

打開ConsoleApp里的Program.cs文件,里面代碼很簡單,定義一個student1類的變量,然后獲取她的一個屬性值並輸出。

View Code
 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Runtime.Serialization;
6
7 namespace ConsoleApp
8 {
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 //定義通信管道 client,就是通過它來調用服務端提供的方法的
14 ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
15 client.student1Class();
16
17
18 //服務端提供給客戶端使用的類
19 ServiceReference1.student1 stu = new ServiceReference1.student1();
20 Console.WriteLine(stu.Str);
21 Console.ReadKey();
22
23
24 }
25 }
26 }

 

ServiceReference1(其實是一個命名空間)就是剛才添加的服務引用的名稱。當輸入ServiceReference1.的時候,后面就會出現智能感知到的這個命名空間能引用的類型、接口等內容(有時候沒有出現如你所願的類型時,右鍵點WcfService1項目選擇重新生成,然后右鍵點ConsoleApp項目的ServiceReference1選擇更新服務引用,這個很容易忘掉,每對服務端做了什么修改都要這么來一下)。圖如下:

里面還定義了一個什么通信管道的東西,這個東西估計就是把服務端提供的可工客戶端引用的方法封裝成了一個類,通過這個類就可以調用那些方法了,詳情見代碼。

最后按F5運行,沒什么問題吧?沒有輸出?這是正常的,如果你想看到輸出結果,可以先對Str屬性賦值(stu.Str = "123";)。嘿嘿,那就算完成了。

 

最后還有兩個問題:

1、在IService1接口里自定義了兩個類 student1 和student2,為什么輸入ServiceReference1.后的智能感知里只有 student1類型而沒有student2類型?因為在IService1接口里有一個方法 public student1 student1Class() ,這個方法的返回值是 studnet1類型,這就是差別。。。。就是你想在客戶使用在服務端里自定義的類型,除了用[DataContract]聲明之外,還要有一個服務契約(方法)的返回值是這個類型的。至於為什么,我也不知道。。求答案

2、student1類里的Str屬性有一個Get{return str;}方法,而字段str是有默認值的,為什么在Program.cs里定義了一個student1類的變量后,獲取Str屬性的值並輸出時是空白?也就是說屬性的默認值發生了丟失!為什么?不知道,求答案。。。

 后來得 亂舞春秋 網友指點,說可能是序列化的原因,嗯,應該就是這個原因,具體的原理學習后下次再慢慢分析。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM