WinVista新技術 WCF開發指南之客戶端開發
作者:朱先忠編譯
一. 客戶端編程為了調用服務上的操作,客戶端首先需要把服務合同導入到客戶端的本地描述中。這意味著,該服務需要客戶端暴露一個標準方法以檢索它的元數據-這是通過讓服務暴露一個元數據交換(MEX)端點來實現的。只要服務在宿主上注冊至少一個TCP、HTTP或IPC基地址,那么,這個基于WCF的服務就能夠自動地暴露一個MEX端點。如果客戶端使用WCF,那么調用操作的普通方法是使用一個代理。這個代理是一個CLR類,它暴露單個描述服務合同的CLR接口。注意,如果該服務支持若干合同,那么客戶端需要對于每一種合同類型都創建一個相應代理。代理負責提供與服務的合同相同的操作,而且還提供其它方法來管理代理的生命周期和到服務的連接。該代理完整地封裝該服務的每一個方面:服務的位置,服務的實現技術及其運行時刻平臺和通訊傳輸。 你可以使用Visual Studio 2005來導入服務元數據并且生成一個代理。如果服務是自宿主的,那么首先要啟動該服務,然后從客戶端工程的上下文菜單中選擇'Add Service Reference…'。如果服務宿主在IIS或WAS上,那么就不需要預啟動該服務了。有趣的是,如果服務是自宿主在與客戶端工程同一個方案中的另一個工程中,那么你可以在Visual Studio 2005中啟動宿主并且仍然添加參考,因為不同于大多數工程設置,這個選項是無法禁止的(見圖6)。這會導致調出'Add Service Reference'對話框,你需要在其中提供服務的基地址(或一個基地址和一個MEX URI)以及包含代理的命名空間。圖6.你可以使用Visual Studio 2005生成一個代理。Visual Studio 2005使用SvcUtil.exe命令行工具,并且你可以自己使用它。主要理由是,你可以使用SvcUtil開關所提供的眾多的選項。為了直接使用SvcUtil,你可以提供給它MEX地址并且,作為選擇,還可以提供一個代理文件名。默認的代理文件名是實現該服務的服務端類的名字。例如,當把服務MyService宿主在IIS中時,簡單地運行下列命令行: SvcUtil http://localhost/MyService/MyService.svc /out:Proxy.cs借助于自宿主,你可以不受限于僅使用HTTP基地址。假定自宿主服務注冊這些基地址:http://localhost:8002net.tcp://localhost:8003net.pipe://localhost/MyPipe然后,啟動宿主,則你可以使用任何下列命令之一來生成該代理:SvcUtil http://localhost:8002/MEX /out:Proxy.csSvcUtil http://localhost:8002/ /out:Proxy.csSvcUtil net.tcp://localhost:8003/MEX /out:Proxy.csSvcUtil net.pipe://localhost/MyPipe /MEX/out:Proxy.cs該代理類沒有對服務實現類的參考,而只參考服務暴露的合同。這個代理可以與一個提供地址和綁定的客戶端配置文件一起使用,也可以在沒有配置文件的情況下使用。注意,每一個代理實例都準確地指向一個端點,與之交互的端點在構造時刻提供給代理。二. 管理客戶端配置客戶端需要知道服務所在地并使用與它的服務相同的綁定;當然,也要以代理的形式導入服務合同。實質上,這與在服務的端點處捕獲的信息完全一致。為了反映這一信息,客戶端配置文件包含關于目標端點的信息并且甚至使用與宿主相同的模式。 例如,列表6(見本文相應下載源碼)顯示了與一個服務(其宿主是根據列表2進行配置的)進行交互需要的客戶端配置文件。注意,在這個客戶端配置文件中的合同類型(和命名空間)是由SvcUtil生成的導入的類型(和命名空間,如果有的話),而不是服務類型和命名空間。該客戶端配置文件可以列出與服務支持一樣多的端點,并且該客戶端可以使用任何其中之一來與該服務交互。列表7(見本文相應下載源碼)展示了客戶端配置文件-它匹配顯示于列表3中的宿主配置文件。默認地,SvcUtil還自動生成一個客戶端配置文件output.config。你可以使用/config開關來指定一個配置文件名:SvcUtil http://localhost:8002/MyService/MEX/out:Proxy.cs /config:App.Config并且,你可以使用/noconfig開關來壓縮生成的配置文件:SvcUtil http://localhost:8002/MyService/MEX/out:Proxy.cs /noconfig為了支持進程內宿主,應用程序配置文件應該列出服務和客戶端節,見列表8(見本文相應下載源碼)。注意,NetNamedPipeBinding被用于進程內調用。WCF提供一個能夠編輯宿主和客戶端配置文件的配置文件編輯器SvcConfigEditor.exe(見圖7)。在寫本文時,SvcConfigEditor只是生成一些不可讀的配置文件,因此,在糾正這一問題之前,你應該手工地編輯這個文件。圖7:SvcConfigEditor用于編輯宿主和客戶端配置文件。
三. 創建和使用代理SvcUtil生成的代理類派生自類ClIEntBase<T>,定義為:
public class ClientBase<T> : IDisposable{ protected ClientBase(string endpointConfigurationName); protected ClientBase(Binding binding,EndpointAddress remoteAddress); public void Close(); public void Dispose(); protected T InnerProxy{get;} //其它成員}這個InnerProxy屬性是客戶端需要消費的合同類型,并且SvcUtil生成的ClientBase<T>的子類簡單地把它代理到方法調用(見列表5)。客戶端需要實例化一個代理對象并且提供給其構造器端點信息-或者是來自配置文件的端點節名(見列表6)或者是在不使用一個配置文件時的端點地址和綁定對象。然后,該客戶端可以使用代理方法來調用該服務,并且客戶端完成后,它需要關閉該代理實例:MyContractProxy proxy = new MyContractProxy('MyEndpoint');proxy.MyMethod();proxy.Close();關閉代理將終止與服務的會話并且關閉連接。作為選擇,你可以使用代理的Dispose()方法來關閉它。Dispose()方法的優點在于,你可以使用using語句來調用它,即使在面臨異常處理時:using(MyContractProxy proxy = new MyContractProxy('MyEndpoint')){ proxy.MyMethod();}在客戶端配置文件中的每個合同類型的一個端點可以被指派為一個默認端點。默認端點是一個沒有名字標志或只有一個空名('')的端點節:<system.serviceModel><client><endpoint...contract='IMyContract' /><endpoint name='OtherEndpoint'...contract='IMyContract' /></client></system.serviceModel>一個默認的端點僅僅是一種理想情況;當創建一個針對默認端點的代理時,你可以使用代理的默認構造器來讓它使用默認端點:MyContractProxy proxy = new MyContractProxy();proxy.MyMethod();proxy.Close();四. 可編程的客戶端配置不依賴于配置文件,客戶端也可以通過編程方式來構建端點并且把它提供給代理構造器。列表9(見本文相應下載源碼)展示了這一技術,其中展示了等價于列表6中的代碼(針對列表2中的服務)。編程配置是很有用的:當端點決策或者是完全動態的-基于當前輸入或在運行時刻使用;或當決策是靜態的且從不更改時,你最好采用硬編碼之。
五. WCF架構到目前為止,本文討論了建立和消費簡單WCF服務所有要求的內容。然而,WCF還為可靠性、事務、安全和實例激活等提供了極其寶貴的支持,所有這些都依賴于WCF基于攔截的架構。讓客戶端與代理交互意味著,WCF總是介于服務和客戶端之間來攔截調用并且執行預調用和調用后處理。當代理把調用堆棧幀串行化為一條消息并且沿著一個通道鏈發送消息時,該解釋即開始。 每一個客戶端通道都要做消息的預調用處理。這個鏈的正確結構和組成在很大程度上依賴于綁定。例如,其中一個通道負責編碼該消息(二進制,文本或MTOM),一個通道負責傳遞安全調用上下文,一個通道負責傳播客戶端事務,一個通道負責管理可靠的會話,一個通道負責加密消息正文(如果這樣配置的話),等等。客戶端的最后一個通道是傳輸通道,它負責把消息由經配置的傳輸傳送到宿主。在宿主端,該消息也經過一個通道鏈,從而實現宿主端消息的預調用處理。在宿主端的第一個通道是傳輸通道,它接收來自傳輸的消息。隨后的通道執行各種任務,例如解密消息正文、譯解消息、把傳播的事務設置到執行線程、設置安全主管、管理會話和激活服務實例。最后一個通道在宿主端把該消息傳遞到調度器。由調度器把該消息值的轉換成一個堆棧幀并且調用服務實例。圖8描述了這一順序。圖8:WCF架構看上去的樣子。在客戶端和服務端的攔截都要確保客戶端和服務得到它們所要求的運行時刻環境以便正確運行。由服務實例執行該調用并且把控制返回到調度器,由它把返回值和錯誤信息(如果有的話)轉換成一條返回消息。現在,整個過程被顛倒:調度器通過宿主端通道傳送消息以執行調用后處理,例如管理事務、撤銷實例、編碼應答信息、加密它,等等。返回的消息轉到傳輸通道,在此把消息發送到客戶端通道以便客戶端調用后處理:解密,編碼,提交或取消事務,等等。由代理把返回的消息轉換成一個堆棧幀并且把控制返回到客戶端。最值得注意的是,在這個架構中的幾乎所有的點都提供了可擴展性鉤子-你可以為專利性行為提供定制通道,定制實例管理或定制安全。事實上,WCF提供的這些標準工具都是使用相同的可擴展性模型實現的。六. 使用通道你可以直接使用通道來調用服務上的操作而甚至不必依賴于一個SvcUtil生成的代理。顯示于列表10(見本文相應下載源碼)中的ChannelFactory<T>類能夠使你任意地創建一個代理。你需要提供給它的構造器端點信息-或者是來自配置文件的端點名,或者是綁定和地址對象,或者是一個端點對象。然后,使用CreateChannel()方法以獲得一個到代理的參考(頂級通道)并且使用它的方法。最后,關閉該代理或者通過把它強制轉換為一個IDisposable接口并且調用其Dispose()方法或強制轉換為一個IClIEntChannel接口并且調用其Close()方法:ChannelFactory<IMyContract> factory;//使用默認的站點factory = new ChannelFactory<IMyContract>('');IMyContract proxy1 = factory.CreateChannel();using(proxy1 as IDisposable){proxy1.MyMethod();}IMyContract proxy2 = factory.CreateChannel();proxy2.MyMethod();IClientChannel clientChannel = proxy2 as IClientChannel;Debug.Assert(clientChannel != null);clientChannel.Close();七. 總結WCF是一種用于構建Windows面向服務的應用程序的SDK。它能夠讓你使用杰出的CLR編程結構(例如類和接口)來發布和消費服務。這種編程模型是聲明性的并且大部分是屬性驅動的。WCF基于攔截的架構提供了內置的工具用于管理服務的許多運行時刻方面;并且,從其應用前景來看,它也是構建Windows分布式應用程序的最具生產效率的方式。WCF的第一個發行版本為開發服務(例如宿主,服務實例管理,異步調用,可靠性,事務管理,非連接性隊列調用和安全性)提供了許多有用的工具。在WCF的第二個發行版本中將添加服務發現以及事件出版和訂閱。WCF要求.NET 2.0環境并將同Windows Vista一同發行。另外,你可以在Windows XP SP2和Windows Server 2003 SP1環境下使用它。
