爲了使讀者對基於WCF的編程模型有一個直觀的映像,我將帶領讀者一步一步地建立一個完整的WCF應用。本應用功能雖然簡單,但它涵蓋了一個完整WCF應用的基本結構。對那些對WCF不是很瞭解的讀者來講,這個例子將帶領你正式進入WCF的世界。html
在這個例子中,咱們將實現一個簡單的計算服務(CalculatorService),提供基本的加、減、乘、除的運算。和傳統的分佈式通訊框架同樣,WCF本質上提供一個跨進程、跨機器以至跨網絡的服務調用。在本例中,客戶端和服務經過運行在相同的同一臺機器上不一樣進程模擬,圖1體現了客戶端和服務端進程互相調用的關係。編程
圖1 計算服務應用運行環境安全
WCF的服務不能孤立地存在,須要寄宿於一個運行着的進程中,咱們把承載WCF服務的進程稱爲宿主,爲服務指定宿主的過程稱爲服務寄宿(Service Hosting)。在咱們的計算服務應用中,採用了兩種服務寄宿方式:經過自我寄宿(Self-Hosting)的方式建立一個控制檯應用做爲服務的宿主(寄宿進程爲Hosting.exe);經過IIS寄宿方式將服務寄宿於IIS中(寄宿進程爲IIS的工做進行W3wp.exe)。客戶端經過另外一個控制檯應用模擬(進程爲Client.exe)。接下來,咱們就一步一步來構建這樣的一個WCF應用。微信
步驟一:構建整個解決方案網絡
經過VS 2008建立一個空白的解決方案,添加以下四個項目。項目的類型、承載的功能和相互引用關係以下,整個項目在VS下的結構如圖2所示。app
- Contracts:一個類庫項目,定義服務契約(Service Contract),引用System.ServiceMode程序集(WCF框架的絕大部分實現和API定義在該程序集中);
- Services:一個類庫項目,提供對WCF服務的實現。定義在該項目中的全部WCF服務實現了定義在Contracts中相應的服務契約,因此Services具備對Contracts項目的引用;
- Hosting:一個控制檯(Console)應用,實現對定義在Services項目中的服務的寄宿,該項目需要同時引用Contracts和Services兩個項目和System.ServiceMode程序集;
- Client:一個控制檯應用模擬服務的客戶端,該項目引用System.ServiceMode程序集。
圖2 計算服務在VS中的結構框架
步驟二:建立服務契約編輯器
WCF採用基於契約的交互方式實現了服務的自治,以及客戶端和服務端之間的鬆耦合。WCF包含四種類型的契約:服務契約、數據契約、消息契約和錯誤契約,這裏着重於服務契約。從功能上講,服務契約抽象了服務提供的全部操做;而站在消息交換的角度來看,服務契約則定義了基於服務調用的消息交換過程當中,請求消息和回覆消息的結構,以及採用的消息交換模式。第4章將提供對服務契約的詳細介紹。分佈式
通常地,咱們經過接口的形式定義服務契約。經過下面的代碼,將一個接口ICalculator定義成服務契約。WCF普遍採用基於自定義特性(Custom Attribtue)的聲明式編程模式,咱們經過在接口上應用System.ServiceModel.ServiceContractAttribute特性將一個接口定義成服務契約。在應用ServiceContractAttribute特性的同時,還能夠指定服務契約的名稱和命名空間。至於契約名稱和命名空間的含義和做用,在本人拙著《WCF技術剖析(卷1)》第4章,在這裏咱們將契約名稱和命名空間設置成CalculatorService和http://www.artech.com/)。ide
經過應用ServiceContractAttribute特性將接口定義成服務契約以後,接口的方法成員並不能自動成爲服務的操做。在此方面,WCF採用的是顯式選擇(Explicit Opt-in)的策略:咱們需要在相應的操做方法上面顯式地應用OperationContractAttribute特性。
1: using System.ServiceModel;
2: namespace Artech.WcfServices.Contracts
3: {
4: [ServiceContract(Name="CalculatorService", Namespace="http://www.artech.com/")]
5: public interface ICalculator
6: {
7: [OperationContract]
8: double Add(double x, double y);
9:
10: [OperationContract]
11: double Subtract(double x, double y);
12:
13: [OperationContract]
14: double Multiply(double x, double y);
15:
16: [OperationContract]
17: double Divide(double x, double y);
18: }
19: }
步驟三:建立服務
當服務契約成功建立時,咱們須要經過實現服務契約來建立具體的WCF服務。WCF服務CalculatorService定義在Services項目中,實現了服務契約接口ICalculator,實現了全部的服務操做。CalculatorService定義以下:
1: using Artech.WcfServices.Contracts;
2: namespace Artech.WcfServices.Services
3: {
4: public class CalculatorService:ICalculator
5: {
6: public double Add(double x, double y)
7: {
8: return x + y;
9: }
10:
11: public double Subtract(double x, double y)
12: {
13: return x - y;
14: }
15:
16: public double Multiply(double x, double y)
17: {
18: return x * y;
19: }
20:
21: public double Divide(double x, double y)
22: {
23: return x / y;
24: }
25: }
26: }
步驟四:經過自我寄宿的方式寄宿服務
WCF服務須要依存一個運行着的進程(宿主),服務寄宿就是爲服務指定一個宿主的過程。WCF是一個基於消息的通訊框架,採用基於終結點(Endpoint)的通訊手段。終結點由地址(Address)、綁定(Binding)和契約(Contract)三要素組成,如圖3所示。因爲三要素應爲首字母分別爲ABC,因此就有了易於記憶的公式:Endpoint = ABC。一個終結包含了實現通訊所必需的全部信息,咱們能夠這樣認識終結點的ABC:
- 地址(Address):地址決定了服務的位置,解決了服務尋址的問題,《WCF技術剖析(卷1)》第2章提供了對地址和尋址機制的詳細介紹;
- 綁定(Binding):綁定實現了通訊的全部細節,包括網絡傳輸、消息編碼,以及其餘爲實現某種功能(好比安全、可靠傳輸、事務等)對消息進行的相應處理。WCF中具備一系列的系統定義綁定,好比BasicHttpBinding、WsHttpBinding、NetTcpBinding等,《WCF技術剖析(卷1)》第3章提供對綁定的詳細介紹;
- 契約(Contract):契約是對服務操做的抽象,也是對消息交換模式以及消息結構的定義。《WCF技術剖析(卷1)》第4章提供對服務契約的詳細介紹。
圖3 終結點三要素
服務寄宿的目的就是開啓一個進程,爲WCF服務提供一個運行的環境。經過爲服務添加一個或多個終結點,使之暴露給潛給的服務消費者。服務消費者最終經過相匹配的終結點對該服務進行調用。咱們能夠徹底經過代碼的方式完成全部的服務寄宿工做,下面的代碼體現了經過一個控制檯應用對CalculatorService的寄宿:
1: using System;
2: using System.ServiceModel;
3: using System.ServiceModel.Description;
4: using Artech.WcfServices.Contracts;
5: using Artech.WcfServices.Services;
6: namespace Artech.WcfServices.Hosting
7: {
8: class Program
9: {
10: static void Main(string[] args)
11: {
12: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
13: {
14: host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:9999/calculatorservice");
15: if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
16: {
17: ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
18: behavior.HttpGetEnabled = true;
19: behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/calculatorservice/metadata");
20: host.Description.Behaviors.Add(behavior);
21: }
22: host.Opened += delegate
23: {
24: Console.WriteLine("CalculaorService已經啓動,按任意鍵終止服務!");
25: };
26:
27: host.Open();
28: Console.Read();
29: }
30: }
31: }
32: }
WCF服務寄宿經過一個特殊的對象完成:ServiceHost。在上面的例子中,基於WCF服務的類型(typeof(CalculatorService))建立了ServieHost對象,並添加了一個終結點。具體的地址爲http://127.0.0.1:9999/calculatorservice,採用了WSHttpBinding,並指定了服務契約的類型ICalculator。
鬆耦合是SOA的一個基本的特徵,WCF應用中客戶端和服務端的鬆耦合體如今客戶端只需要瞭解WCF服務基本的描述,而無須知道具體的實現細節,就能夠實現正常的服務調用。WCF服務的描述經過元數據(Metadata)的形式發佈出來。WCF中元數據的發佈經過一個特殊的服務行爲ServiceMetadataBehavior實現。在上面提供的服務寄宿代碼中,咱們爲建立的ServiceHost添加了ServiceMetadataBehavior,並採用了基於HTTP-GET的元數據獲取方式,元數據的發佈地址經過ServiceMetadataBehavior的HttpGetUrl指定。在調用ServiceHost的Open方法對服務成功寄宿後,咱們能夠經過該地址獲取服務相關的元數據。在IE地址欄上鍵入http://127.0.0.1:9999/calculatorservice/metadata,你將會獲得以WSDL形式體現的服務元數據,如圖4所示。
圖4 經過HTTP-GET的方式獲取WCF服務的元數據
在進行真正的WCF應用開發時,通常不會直接經過編碼的方式進行終結點的添加和服務行爲的定義,而是經過配置的方式進行。上面添加終結點和定義服務行爲的代碼能夠用下面的配置代替:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <serviceBehaviors>
6: <behavior name="metadataBehavior">
7: <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9999/calculatorservice/metadata" />
8: </behavior>
9: </serviceBehaviors>
10: </behaviors>
11: <services>
12: <service behaviorConfiguration="metadataBehavior" name="Artech.WcfServices.Services.CalculatorService">
13: <endpoint address="http://127.0.0.1:9999/calculatorservice" binding="wsHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" />
14: </service>
15: </services>
16: </system.serviceModel>
17: </configuration>
對於初學者來講,WCF的配置顯得過於複雜,直接對配置文件進行手工編輯不太現實。在這種狀況下,能夠直接使用VS提供的配置工具。你能夠經過VS的工具(Tools)菜單,選擇「WCF Service Configuration Editor」子項,開啓這樣的一個配置編輯器,如圖5所示。
若是採用了上訴的配置,服務寄宿代碼將會獲得極大的精簡,只需包含下面幾行代碼:
1: namespace Artech.WcfServices.Hosting
2: {
3: class Program
4: {
5: static void Main(string[] args)
6: {
7: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
8: {
9: host.Opened += delegate
10: {
11: Console.WriteLine("CalculaorService已經啓動,按任意鍵終止服務!");
12: };
13:
14: host.Open();
15: Console.Read();
16: }
17: }
18: }
19: }
圖5 如何得到WCF服務配置編輯器
步驟五:建立客戶端調用服務
服務被成功寄宿後,服務端便開始了服務調用請求的監聽工做。此外,服務寄宿將服務描述經過元數據的形式發佈出來,相應的客戶端就能夠獲取這些元數據建立客戶端程序進行服務的消費。在VS下,當咱們添加服務引用的時候,VS在內部幫咱們實現元數據的獲取,並藉助這些元數據經過代碼生成工具(SvcUtil.exe)自動生成用於服務調用的服務代理相關的代碼和相應的配置。
在運行服務寄宿程序(Hosting.exe)的狀況下,右鍵點擊Client項目,在彈出的上下文菜單中選擇「添加服務引用(Add Service References)」,如圖6所示的添加服務引用的對話會顯示出來。在地址欄上鍵入服務元數據發佈的源地址:http://127.0.0.1:9999/calculatorservice/metadata,並指定一個命名空間,點擊OK按鈕,VS爲爲你生成一系列用於服務調用的代碼和配置。
圖6 添加服務引用
在一系列自動生成的類中,包含一個服務契約接口、一個服務代理對象和其餘相關的類。被客戶端直接用於服務調用的是一個繼承自ClientBase<CalculatorService>並實現了CalculatorService接口(CalculatorService爲客戶端生成的服務契約接口類型)的服務代理類。ClientBase<CalculatorService>的定義以下所示:
1: namespace Artech.WcfServices.Client.CalculatorServices
2: {
3: //其餘類型成員
4: [System.Diagnostics.DebuggerStepThroughAttribute()]
5: [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
6: public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<Artech.WcfServices.Client.CalculatorServices.CalculatorService>, Artech.WcfServices.Client.CalculatorServices.CalculatorService {
7:
8: public CalculatorServiceClient() {
9: }
10:
11: public CalculatorServiceClient(string endpointConfigurationName) :
12: base(endpointConfigurationName) {
13: }
14:
15: public CalculatorServiceClient(string endpointConfigurationName, string remoteAddress) :
16: base(endpointConfigurationName, remoteAddress) {
17: }
18:
19: public CalculatorServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
20: base(endpointConfigurationName, remoteAddress) {
21: }
22:
23: public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
24: base(binding, remoteAddress) {
25: }
26:
27: public double Add(double x, double y) {
28: return base.Channel.Add(x, y);
29: }
30:
31: public double Subtract(double x, double y) {
32: return base.Channel.Subtract(x, y);
33: }
34:
35: public double Multiply(double x, double y) {
36: return base.Channel.Multiply(x, y);
37: }
38:
39: public double Divide(double x, double y) {
40: return base.Channel.Divide(x, y);
41: }
42: }
咱們能夠建立CalculatorServiceClient對象,執行相應方法調用服務操做。客戶端進行服務調用的代碼以下:
1: using System;
2: using Artech.WcfServices.Client.CalculatorServices;
3: namespace Artech.WcfServices.Client
4: {
5: class Program
6: {
7: static void Main(string[] args)
8: {
9: using (CalculatorServiceClient proxy = new CalculatorServiceClient())
10: {
11: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2));
12: Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2));
13: Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2));
14: Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2));
15: }
16: }
17: }
18: }
運行後輸出:
x + y = 3 when x = 1 and y = 2
x - y = -1 when x = 1 and y = 2
x * y = 2 when x = 1 and y = 2
x / y = 0.5 when x = 1 and y = 2
客戶端經過服務代理對象進行服務的調用,上面的例子經過建立自動生成的、繼承自ClientBase<T>的類型對象進行服務調用。實際上,咱們還具備另一種建立服務代理的方法,就是經過ChannelFactory<T>。此外,WCF採用基於契約的服務調用方法,從上面的例子咱們也能夠看到,VS在進行服務引用添加的過程當中,會在客戶端建立一個與服務端等效的服務契約接口。在咱們的例子中,因爲服務端和客戶端都是在同一個解決方案中,徹底可讓服務端和客戶端引用相同的契約。
爲了演示這種場景,咱們將添加的服務引用移除,併爲Client項目添加對Contracts項目的引用。藉助於這個服務契約,並經過ChannelFactory<ICalculator>建立服務代理對象,直接進行相應的服務調用。下面的代碼演示了基於ChannelFacotory<T>進行服務代理的建立和服務調用的方式。
1: using System;
2: using System.ServiceModel;
3: using Artech.WcfServices.Contracts;
4: namespace Artech.WcfServices.Client
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(new WSHttpBinding(), "http://127.0.0.1:9999/calculatorservice"))
11: {
12: ICalculator proxy = channelFactory.CreateChannel();
13: using (proxy as IDisposable)
14: {
15: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2));
16: Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2));
17: Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2));
18: Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2));
19: }
20: }
21: }
22: }
23: }
因爲終結點是WCF進行通訊的惟一手段,ChannelFactory<T>本質上是經過指定的終結點建立用於進行服務調用的服務代理。在上面的代碼中,在建立ChannelFactory<T>的時候再在構造函數中指定終結點的相關要素(契約經過範型類型表示,地址和綁定則經過參數指定)。在真正的WCF應用中,大都採用配置的方式進行終結點的定義。咱們能夠經過下面的配置指定終結點的三要素,併爲相應的終結點指定一個終結點配置名稱(calculatorservice)。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint address="http://127.0.0.1:9999/calculatorservice" binding="wsHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" name="calculatorservice" />
6: </client>
7: </system.serviceModel>
8: </configuration>
那麼在建立ChannelFactory<T>的時候,就無須再指定終結點的綁定和地址了,而只須制定對應的終結點配置名稱。
1: using System;
2: using System.ServiceModel;
3: using Artech.WcfServices.Contracts;
4: namespace Artech.WcfServices.Client
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>( "calculatorservice"))
11: {
12: //省略代碼
13: }
14: }
15: }
16: }
步驟六:經過IIS寄宿服務
上面演示了經過自我寄宿的方式寄宿服務,如今咱們來演示如何將WCF服務寄宿到IIS中。寄宿IIS的服務寄宿比較簡單,基本上包含兩個步驟:爲WCF服務建立.svc文件和建立IIS虛擬目錄。
一、爲WCF服務建立.svc文件
咱們知道,每個ASP.NET Web服務都具備一個.asmx文本文件,客戶端經過訪問.asmx文件實現對相應Web服務的調用。與之相似,每一個WCF服務也具備一個對應的文本文件,其文件擴展名爲.svc。基於IIS的服務寄宿要求相應的WCF服務具備相應的.svc文件,.svc文件部署於IIS站點中,對WCF服務的調用體如今對.svc文件的訪問上。
.svc文件的內容很簡單,僅僅包含一個ServiceHost指令(Directive),該指令具備一個必須的Service屬性和一些可選的屬性。因此最簡單的.svc僅僅具備一個包含Service屬性(該屬性指明瞭相應的WCF服務的有效類型)的ServiceHost指令。CalculatorService對應的.svc以下所示,咱們把該.svc放在Services項目的根目錄下,並將文件命名爲CalculatorService.svc。
1: <%@ServiceHost Service="Artech.WcfServices.Services.CalculatorService"%>
二、爲WCF服務建立虛擬目錄
和通常的寄宿於IIS下的Web應用同樣,須要在IIS下建立相應的虛擬目錄。在本應用中,爲了方便,咱們直接把Services項目的根目錄映射爲IIS虛擬目錄,並把該虛擬目錄的命名爲WcfServices。
接下來須要爲經過IIS寄宿的CalculatorService建立配置文件,咱們只須在Services的根目錄下建立一個Web.config,將WCF相應的配置添加到該配置文件中便可。Web.config全部配置內容以下所示,能夠看出,這基本上和上面經過自我寄宿方式定義的配置一致。惟一不一樣的是在添加的終結點中無須指定地址,由於.svc所在的地址就是服務的地址。也就是說,CalculatorService的地址爲http://127.0.0.1/wcfservices/calculatorservice.svc。你能夠經過http://127.0.0.1/wcfservices/calculatorservice.svc?wsdl獲得相應的元數據。因爲WSHttpBinding在默認狀況下采用Windows認證,因此在IIS中將Windows集成認證開啓。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <serviceBehaviors>
6: <behavior name="metadataBehavior">
7: <serviceMetadata httpGetEnabled="true"/>
8: </behavior>
9: </serviceBehaviors>
10: </behaviors>
11: <services>
12: <service behaviorConfiguration="metadataBehavior" name="Artech.WcfServices.Services.CalculatorService">
13: <endpoint binding="wsHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" />
14: </service>
15: </services>
16: </system.serviceModel>
17: </configuration>
因爲在建立Services項目的時候,咱們並未曾引用System.ServiceMode程序集,因此需要加上這樣一個引用。此外,一個Web應用在運行的時候會默認從位於根目錄下的Bin目錄加載程序集,而默認的狀況下,咱們編譯後的程序集會自動保存到Bin\Debug|Release目錄下,因此需要經過VS修改Services項目屬性,將編譯輸出目錄設置成Bin。
客戶端僅僅需要修改終結點的地址,從而轉向對寄宿於IIS下的CalculatorService的訪問,該地址即爲.svc文件的網絡地址:http://127.0.0.1/wcfservices/calculatorservice.svc。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint address="http://127.0.0.1/wcfservices/calculatorservice.svc" binding="wsHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" name="calculatorservice" />
6: </client>
7: </system.serviceModel>
8: </configuration>
摘自;蔣金楠
微信公衆帳號:大內老A
微博:www.weibo.com/artech