创建一个简单的WCF程序
為了使讀者對基于WCF的編程模型有一個直觀的映像,我將帶領讀者一步一步地創建一個完整的WCF應用。本應用功能雖然簡單,但它涵蓋了一個完整WCF應用的基本結構。對那些對WCF不是很了解的讀者來說,這個例子將帶領你正式進入WCF的世界。
在這個例子中,我們將實現一個簡單的計算服務(CalculatorService),提供基本的加、減、乘、除的運算。和傳統的分布式通信框架一 樣,WCF本質上提供一個跨進程、跨機器以致跨網絡的服務調用。在本例中,客戶端和服務通過運行在相同的同一臺機器上不同進程模擬,圖1體現了客戶端和服務端進程互相調用的關系。
圖1 計算服務應用運行環境
WCF的服務不能孤立地存在,需要寄宿于一個運行著的進程中,我們把承載WCF服務的進程稱為宿主,為服務指定宿主的過程稱為服務寄宿 (Service Hosting)。在我們的計算服務應用中,采用了兩種服務寄宿方式:通過自我寄宿(Self-Hosting)的方式創建一個控制臺應用作為服務的宿主 (寄宿進程為Hosting.exe);通過IIS寄宿方式將服務寄宿于IIS中(寄宿進程為IIS的工作進行W3wp.exe)。客戶端通過另一個控制 臺應用模擬(進程為Client.exe)。接下來,我們就一步一步來構建這樣的一個WCF應用。
步驟一:構建整個解決方案
通過VS 2008創建一個空白的解決方案,添加如下四個項目。項目的類型、承載的功能和相互引用關系如下,整個項目在VS下的結構如圖2所示。
- 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/)。
通過應用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虛擬目錄。
1、為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"%>2、為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> 作者:Artech出處:http://artech.cnblogs.com/
轉載于:https://www.cnblogs.com/cmblogs/p/3374661.html
總結
以上是生活随笔為你收集整理的创建一个简单的WCF程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: delphi中TStringGrid数据
- 下一篇: 英语语法学习2--句子的成分