一、引言
前面幾篇文章分享了.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特性將一個接口定義成服務契約。在應用該特性的同時,還可以指定服務契約的名稱和命名空間,在這個服務契約中,我們將契約名稱和命名空間設置成HellworldService和http://www.Learninghard.com 。應用ServiceContractAttribute特性將接口定義成服務契約之后,接口中定義的方法也不能自動成為服務的操作方法。此時,我們需要在相應的方法上顯式地應用OperationContractAttribute特性。具體服務契約的實現代碼如下所示:
1 [ServiceContract(Name = "HellworldService", Namespace = "http://www.Learninghard.com")] 2 public interface IHelloWorld 3 { 4 [OperationContract()] 5 string GetHelloWorld(); 6 }
服務契約成功創建之后,我們需要實現服務契約來創建具體的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對象來完成的。在上面代碼中,WCF實例是通過基於WCF服務的類型(typeof(HelloWorldService))來創建的,並通過AddServiceEndpoint添加了一個終結點,具體終結點的地址為http://127.0.0.1:8888/HelloWorldService,采用的綁定(Binding)類型為WSHttpBinding,並指定了服務契約的類型為IHelloWorld。
WCF是SOA的實現,而SOA的一個基本特征是松耦合,WCF應用中客戶端和服務端的松耦合體現在客戶端只需了解WCF服務基本的描述,而無需知道具體的實現細節就可以實現對服務的訪問。WCF服務的描述通過元數據的形式進行發布的。而WCF元數據的發布是通過一個服務行為ServiceMetadataBehavior來實現的。在上面代碼中,我們為創建的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福門教程疑難解答。
而在進行真正的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。所以對應的.svc文件內容如下所示:
<%@ ServiceHost Language="C#" Debug="true" Service="Services.HelloWorldService" %>
具體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