# 跟我一起學WCF(4)——第一個WCF程序
## 一、引言
前面幾篇文章分享了.NET 平臺下其他幾種分布式技術,然而前面幾種分布式技術專注于某一特定的領域,并且具有不同編程接口,這使得開發人員需要掌握多個API的使用。基于這樣的原因,微軟在.NET 3.0時實現了WCF。WCF是.NET平臺下各種分布式技術的集成,它將前面介紹的幾種分布式技術完全整合在一起,并提供了一套統一的編程接口(API)。對于,開發人員來來說只需要掌握WCF一套的API,就可以實現之前分布式技術所實現的所有功能。
## 二、WCF詳細介紹
WCF(Windows Communication Foundation)是微軟為構建面向服務的應用程序(SOA)而提供的統一編程模型,借助該模型,使得在構建分布式系統中,無需再考慮如何去實現通信的相關的問題,使開發人員更加關注與系統業務邏輯本身的實現。而在WCF中,各個Application之間的通信是由Endpoint(終結點)來實現的。下面詳細介紹下WCF幾個重要的概念。
## 2.1 EndPoint 詳細介紹
服務的提供者將服務通過一個或多個終結點發布給潛在的服務消費者,服務的消費者則通過與之匹配的終結點對服務進行消費。終結點由地址(Address)、綁定(Binding)和契約(Contract)三要素組成。如下圖所示。由于它們的首字母分別是A、B、C。所以就有了:EndPoint = ABC。

這三個要素在WCF通信中起到的作用分別是:
* **地址(Address)**:地址標識了服務的位置,提供尋址的輔助信息和標識了服務的真實身份。Address解決了**Where the WCF service?**的問題。
* **綁定(Binding)**:綁定實現了通信的所有細節,包括網絡傳輸,消息編碼,以及其他為實現某種功能對消息進行的相應處理,例如安全、可靠傳輸和事務等功能。 WCF中具有一系列的系統已定義的綁定,如BasicHttpBinding、WsHttpBinding、NetTcpBinding等。Binding解決了How to Communicate with Service?的問題。
* **契約(Contract)**:契約是對服務操作的抽象,也是對消息交互模式以及消息結構的定義。WCF的契約大體可以分為兩類,一類是對服務操作的描述;另一類是對數據的描述。服務契約(Service Contract)則屬于對服務操作的描述,而后一類包括其余3中契約:數據契約(Data Contract)、消息契約(Message Contract)和錯誤契約(Fault Contract)。Contract解決了What function does the Service Provide?的問題。
## 2.2 WCF 基礎概念
* 消息模式
消息時一個獨立的數據單元,它可能由幾個部分組成,包括消息正文和消息頭。WCF支持多種消息模式,包括請求-恢復、單向和雙工通信。不同傳輸協議支持不同的消息模式,WCF API和運行庫還能保證安全而可靠地發送消息
* 通信協議和編碼
WCF支持Http、TCP、Peer network(對等網)、IPC(基于命名管道的內部進程通信)和MSMQ協議。在進行消息傳遞之前,必須對給定的消息進行格式化的編碼,WCF提供了3種編碼選擇:一種是文本編碼,一種跨平臺的編碼;一種是消息傳輸優化機制(MOMO)編碼,該編碼用于高效地將非結構化的二進制數據發送到服務或從服務接收這些數據。一種是用于實現高效傳輸的二進制編碼。
* 行為(Behavior):Behavior的主要作用是定制EndPoint在運行時的一些不要的行為。比如Service回調Client的TimeOut屬性的設置。
* 宿主和宿主進程:服務必須承載于某個進程中,宿主進程是專為承載服務而設計的應用程序,這些宿主進程包括Internet信息服務(即IIS)、Windows激活服務(WAS)、Windows服務。由宿主控制服務的生命周期。
* 自承載服務:服務除了可以由現有的宿主進程承載外,還可以自承載,自承載服務是由開發人員創建的進程應用程序來承載服務。該應用程序控制服務的聲明周期,設置服務的屬性和打開服務和關閉服務等操作。
## 三、創建第一個WCF應用程序
前面介紹了WCF的詳細內容,接下來,我們采用以下兩種服務寄宿方法來創建WCF應用程序。
* 通過自我寄宿(Self-Hosting)的方式,即自承載服務。創建一個控制臺應用程序來作為服務的宿主。
* 通過IIS寄宿方式將服務寄宿在IIS中。客戶端通過另一個控制臺程序來模擬客戶端來對服務進行調用。
接下來,我們一步一步來使用兩種方式來實現我們的WCF應用程序。首先介紹自我寄宿方式的實現步驟。
### 步驟一:創建服務契約和服務
既然是分布式應用程序,首先第一步肯定是創建供其他消費者消費的服務應用程序。而WCF采用基于契約的交互方式實現了服務的自治,以及客戶端和服務端之間的松耦合。從功能角度上,服務契約抽象了服務提供的所有操作,所以,我們一般通過接口的形式定義服務契約。WCF通過在接口上應用[System.ServiceModel.Service.ServiceContractAttribute](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.servicecontractattribute(v=vs.110).aspx)特性將一個接口定義成服務契約。在應用該特性的同時,還可以指定服務契約的名稱和命名空間,在這個服務契約中,我們將契約名稱和命名空間設置成HellworldService和http://www.Learninghard.com 。應用ServiceContractAttribute特性將接口定義成服務契約之后,接口中定義的方法也不能自動成為服務的操作方法。此時,我們需要在相應的方法上顯式地應用[OperationContractAttribute](http://msdn.microsoft.com/zh-cn/library/System.ServiceModel.OperationContractAttribute(v=vs.110).aspx)特性。具體服務契約的實現代碼如下所示:
服務契約成功創建之后,我們需要實現服務契約來創建具體的WCF服務。WCF服務的具體實現代碼如下所示:
```
1 public class HelloWorldService : IHelloWorld
2 {
3 public string GetHelloWorld()
4 {
5 return "Hello World";
6 }
7 }
```
### 步驟二:創建服務宿主
前面介紹到,WCF服務必須寄存在某個進程中,該進程稱為宿主應用程序。服務寄宿的目的就是開啟一個進程來為WCF服務提供一個運行的環境。通過為服務添加一個或多個終結點,使之暴露給服務消費者使用。服務消費者再通過相應匹配的終結點對服務進行調用,下面通過創建一個控制臺程序來實現WCF服務的自我寄宿方式,具體的實現代碼如下所示:
```
1 using Contract;
2 using Services;
3 using System;
4 using System.ServiceModel;
5 using System.ServiceModel.Description;
6
7 namespace Hosting
8 {
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 using (ServiceHost host = new ServiceHost(typeof(HelloWorldService)))
14 {
15 // 如果采用配置文件的方式,Region中代碼就可以注釋點
16 #region
17 host.AddServiceEndpoint(typeof(IHelloWorld), new WSHttpBinding(), "http://127.0.0.1:8888/HelloWorldService");
18 if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
19 {
20 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
21 behavior.HttpGetEnabled = true;
22 behavior.HttpGetUrl = new Uri("http://127.0.0.1:8888/HelloWorldService/metadata");
23 host.Description.Behaviors.Add(behavior);
24 }
25 #endregion
26
27 host.Opened += delegate
28 {
29 Console.WriteLine("HelloWorldService 已經啟動, 按任意鍵終止服務!");
30 };
31
32 host.Open();
33 Console.Read();
34 }
35 }
36 }
37 }
```
WCF服務寄宿通過[ServiceHost](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.servicehost(v=vs.110).aspx)對象來完成的。在上面代碼中,WCF實例是通過基于WCF服務的類型(typeof(HelloWorldService))來創建的,并通過AddServiceEndpoint添加了一個終結點,具體終結點的地址為http://127.0.0.1:8888/HelloWorldService,采用的綁定(Binding)類型為WSHttpBinding,并指定了服務契約的類型為IHelloWorld。
WCF是SOA的實現,而SOA的一個基本特征是松耦合,WCF應用中客戶端和服務端的松耦合體現在客戶端只需了解WCF服務基本的描述,而無需知道具體的實現細節就可以實現對服務的訪問。WCF服務的描述通過元數據的形式進行發布的。而WCF元數據的發布是通過一個服務行為[ServiceMetadataBehavior](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.description.servicemetadatabehavior(v=vs.110).aspx)來實現的。在上面代碼中,我們為創建的ServiceHost對象添加了ServiceMetadataBehavior,并采用了基于Http-GET的元數據獲取方式,元數據的發布地址指定為http://127.0.0.1:8888/HelloWorldService/metadata。在調用ServiceHost的Open方法對服務成功寄宿后,你可以通過該地址獲取服務相關的元數據,就如Web 服務中通過輸入WSDL地址來獲得Web服務的描述一樣。當我們成功運行ConsoleAppHosting宿主應用程序后,在瀏覽器中輸入http://127.0.0.1:8888/HelloWorldService/metadata這個地址,你將得到如下所示的服務元數據。

在運行宿主應用程序時,如果你沒有以管理員權限運行宿主應用程序的話,你將會得到Http無法注冊的異常,具體異常信息如下圖所示:

此時,如果你是在VS中運行宿主程序,你就需要以管理員權限運行VS2012,如果直接在文件夾中運行宿主程序,此時需要右鍵宿主程序的exe文件,選擇以管理員身份運行就不會出現如上圖所示的異常。MSDN詳細解釋連接為:[WCF福門教程疑難解答](http://msdn.microsoft.com/zh-cn/library/bb924513.aspx#bkmk_q2)。
而在進行真正的WCF應用開發時,都不會直接通過硬編碼的方式進行終結點的添加和服務行為的定義,而是通過配置文件的方式來進行的。你可以以下面配置文件的方式來代替上面的硬編碼方式。
```
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8888/HelloWorldService/metadata"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="metadataBehavior" name ="Services.HelloWorldService">
<endpoint address="http://127.0.0.1:8888/HelloWorldService"
binding="wsHttpBinding"
contract="Contract.IHelloWorld"/>
</service>
</services>
</system.serviceModel>
</configuration>
```
### 步驟三:創建客戶端
服務成功寄宿之后,服務端便開始了服務調用請求的監聽工作。另外,服務寄宿將服務描述通過元數據的方式發布出來,相應的客戶端就可以獲取這些元數據來創建客戶端程序來對服務進行調用。在Visual Studio下,當我們添加服務引用時,VS在內部會幫我們實現元數據的獲取,并通過代碼生成工具(SvcUtil.exe)將這些元數據自動生成用于服務調用的服務代理相關的代碼和相應的配置。
在成功運行服務寄宿程序后,右鍵客戶端項目,在彈出的菜單中選擇“添加服務引用”,然后在彈出的添加服務引用窗口中輸入服務元數據的地址:http://127.0.0.1:8888/HelloWorldService/metadata,并指定一個命名空間,點擊確定按鈕(具體效果如下圖所示),VS將為你生成用于服務調用的代理類代碼和配置信息。

添加成功之后,我們可以通過創建服務代理類對象來對服務相應方法進行調用操作,客戶端進行服務調用的具體實現代碼如下所示:
```
1 using Client.HelloWorldServices;
2 using System;
3
4 namespace Client
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 // HelloworldServiceClient就是VS為我們創建的服務代理類
11 using (HellworldServiceClient proxy = new HellworldServiceClient())
12 {
13 // 通過代理類來調用進行服務方法的訪問
14 Console.WriteLine("服務返回的結果是: {0}", proxy.GetHelloWorld());
15 }
16
17 Console.Read();
18 }
19 }
20 }
```
運行客戶端程序后,你將獲得如下圖所示的運行結果。

上面演示了通過自我寄宿的方式來寄宿服務,接下來我們來演示如何將WCF服務寄宿到IIS中。因為WCF服務和服務契約在上面方式中已實現,所以IIS寄宿方式的包含兩個步驟:創建IIS宿主服務和創建客戶端調用程序。下面分別介紹下這兩個步驟。
### 步驟一:創建IIS宿主服務
在開始的解決方案中,創建一個Asp.net空Web應用程序,然后添加一個WCF服務文件。這里WCF服務文件與Web 服務中的.asmx文件類似。基于IIS的服務寄宿要求相應的WCF服務具有相應的.svc文件,.svc文件部署于IIS站點中,對WCF服務的調用體現在對.svc文件的訪問上。
WCF服務文件的內容很簡單,僅僅包含一個ServiceHost指令,該指令具有一個必須的Service屬性和一些可選的屬性。詳細信息見MSDN:[@ServiceHost](http://msdn.microsoft.com/zh-cn/library/aa967286(v=vs.110).aspx)。所以對應的.svc文件內容如下所示:
具體Web.Config的配置內容如下所示:
```
<configuration>
<system.web>
<compilation debug="true"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="metadataBehavior" name ="Services.HelloWorldService">
<endpoint binding="wsHttpBinding"
contract="Contract.IHelloWorld"/>
</service>
</services>
</system.serviceModel>
</configuration>
```
從上面配置內容可以看出,這基本上和自我寄宿方式的配置文件一致,唯一不同的是在添加終結點中無需指定地址,因為.svc所在的地址就是服務的地址。
### 步驟二:創建客戶端程序
此時,客戶端僅僅需要修改終結點地址來對寄宿于IIS下的HellworldService進行訪問,該地址為:http://localhost:15826/HelloWorldService.svc。此時可以http://localhost:15826/HelloWorldService.svc?wsdl得到相應的元數據。具體客戶端代碼的實現如下所示:
```
1 using Client2.HelloWorldService;
2 using System;
3
4 namespace Client2
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 using (HellworldServiceClient proxy = new HellworldServiceClient())
11 {
12 Console.WriteLine("服務返回的結果是: {0}", proxy.GetHelloWorld());
13 }
14
15 Console.Read();
16 }
17 }
18 }
```
具體的配置文件內容如下所示:
```
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_HellworldService" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:15826/HelloWorldService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_HellworldService"
contract="HelloWorldService.HellworldService" name="WSHttpBinding_HellworldService">
</endpoint>
</client>
</system.serviceModel>
</configuration>
```
運行客戶端2,得到的運行結果與自我寄宿方式得到的結果一樣。這里就不貼出結果圖了。
## 四、總結
到這里,本篇文章的分享就結束。本文首先通過介紹WCF相關的基礎概念,其中最重要的莫過于終結點和組成它的三個元素,之后分別介紹自我寄宿和IIS寄宿方式來創建WCF應用程序,在平常開發過程中,用到最多是通過IIS寄宿方式來對服務進行寄宿。在一篇文章中將分享關于WCF服務契約的內容。
本文所有源代碼下載地址:[FirstWCFApp.zip](http://files.cnblogs.com/zhili/FirstWCFApp.zip)
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結