?
Windows Workflow Foundation
?
Windows Communication Foundation
?
Microsoft Visual Studio 2005
摘要:本文簡(jiǎn)要介紹如何在通過(guò) Windows Communication Foundation (WCF) 創(chuàng )建的服務(wù)內托管用 Windows Workflow Foundation (WF) 所構建的工作流。本文還將介紹如何利用 WCF 所提供的眾多功能中的幾個(gè),來(lái)通過(guò)雙工信道簡(jiǎn)化客戶(hù)端事件回調。(本文還包含指向英文網(wǎng)頁(yè)的鏈接。)
本頁(yè)內容
簡(jiǎn)介開(kāi)支報告示例集成工作檢查表定義服務(wù)約定托管工作流運行時(shí)配置部署使用服務(wù)配置雙工信道結束語(yǔ)更多信息致謝單擊
此處下載“Windows Workflow Foundation 示例:WF 與 WCF 集成”。
簡(jiǎn)介
隨著(zhù) Windows Workflow Foundation (WF) 的問(wèn)世,Microsoft 逐步將各種工作流功能引入了 .NET 開(kāi)發(fā)人員平臺。這些功能使開(kāi)發(fā)人員能夠構建用于滿(mǎn)足各種應用需求的工作流,從簡(jiǎn)單的順序工作流到需要復雜的人員交互的復雜狀態(tài)機工作流。
與此同時(shí),業(yè)務(wù)能力越來(lái)越多地通過(guò)封裝的服務(wù)端點(diǎn)展現出來(lái),這樣就可以重用和組合業(yè)務(wù)功能和業(yè)務(wù)流程,使面向服務(wù)的體系架構更加完善。Windows Communication Foundation (WCF) 提供了統一的開(kāi)發(fā)人員 API、穩健的托管運行時(shí)和靈活的配置驅動(dòng)解決方案來(lái)幫助進(jìn)行部署,進(jìn)而幫助開(kāi)發(fā)人員通過(guò)各種功能來(lái)輕松開(kāi)發(fā)互聯(lián)系統。
本文在結尾處列出了一些可用來(lái)進(jìn)一步了解 WF 和 WCF 的附加資源。
返回頁(yè)首開(kāi)支報告示例
本文的代碼示例基于為員工報銷(xiāo)申請提交與審批標準業(yè)務(wù)流程建立模型的“開(kāi)支報告”工作流示例。我們在原示例基礎上進(jìn)行了更新,用以說(shuō)明如何利用 WCF 和 .NET 3.0 Framework 來(lái)更有效地托管這一業(yè)務(wù)環(huán)節。
在發(fā)行的第一版“開(kāi)支報告”示例中,使用 .NET 遠程調用來(lái)提供客戶(hù)端應用程序與包含工作流運行時(shí)實(shí)例的宿主應用程序之間的通信。
而經(jīng)我們重構的開(kāi)支報告示例在實(shí)現時(shí)使用 WCF 來(lái)執行客戶(hù)端和服務(wù)端之間的通信。此外,該解決方案還實(shí)現了邏輯上的結構化,從而將其中的各種問(wèn)題分離出來(lái)。
圖 1. 重構后解決方案的結構
了解消息在業(yè)務(wù)流程上下文中的使用方式非常重要,只有這樣您才能將它們融入設計之中。在“開(kāi)支報告”的生命周期中,存在幾個(gè)交互點(diǎn)。我們來(lái)簡(jiǎn)單分析一下:
?
這一流程涉及到三方:客戶(hù)端、管理人員和開(kāi)支報告宿主系統。
?
這一流程從客戶(hù)端提交新的報銷(xiāo)申請開(kāi)始。
?
我們使用一種規則策略來(lái)確定報銷(xiāo)申請是否可以自動(dòng)審批。
?
如果報銷(xiāo)申請不能自動(dòng)審批,則需要管理人員審批該報告。管理人員要么需要自行檢查是否有待審批的新報告,要么需要在有待審批的新報告時(shí)獲得通知。
?
如果管理人員在靈活的延遲時(shí)間內未進(jìn)行審批,該流程會(huì )自動(dòng)拒絕此報銷(xiāo)申請。
?
報銷(xiāo)申請被復審完畢后,客戶(hù)端和管理人員必須被及時(shí)告知結果。
通過(guò) WF,我們可以利用該框架所提供的標準活動(dòng)來(lái)為這一流程建模。我們可以使用 DelayActivity 來(lái)管理在一段時(shí)間后觸發(fā)的事件,可以使用規則引擎和 PolicyActivity 來(lái)管理一套靈活的規則,通過(guò)詢(xún)問(wèn)這些規則可以獲得結果。
由于這是一個(gè)面向人員的流程,因此我們必須與最終用戶(hù)進(jìn)行交互,并將該交互交回到工作流中。WF 提供了“本地服務(wù)”、HandleExternalEventActivity 和 CallExternalMethodActivity,從而為實(shí)現宿主和工作流之間的通信提供了全面的編程模型。
由于這對于構建交互式工作流而言是一個(gè)重要的概念,因此讓我們快來(lái)了解一下它是如何被設計到 WF 中的。
為了在 WF 中建立用戶(hù)交互的模型,我們必須設計用于提供許多事件和方法的約定。工作流和宿主進(jìn)程都應理解這一約定。所構建的約定/接口必須以 [ExternalDataExchange()] 屬性來(lái)標記,這一屬性會(huì )將其標識為專(zhuān)用于進(jìn)行工作流數據交換。在我們所列舉的示例中,工作流使用 IExpenseLocalService 接口。
然后我們訂閱一個(gè)與工作流運行時(shí)實(shí)現該接口的類(lèi)(稱(chēng)為本地服務(wù))。工作流活動(dòng)可以注冊到事件或者使用接口類(lèi)型上定義的方法,并將綁定到我們已經(jīng)注冊的本地服務(wù)。這采用的是一種稱(chēng)為“控制反轉”的模式,這種模式消除了工作流與具體類(lèi)型本地服務(wù)之間的緊耦合。在我們所列舉的示例中,ExpenseLocalService 類(lèi)實(shí)現了 IExpenseLocalService 約定。
工作流在第一次運行時(shí),可以獲得一個(gè)用于執行操作的初始數據包。當工作流到達需要外部交互的點(diǎn)后,我們可以引發(fā)一個(gè)能夠在工作流中綁定到 HandleExternalEventActivity 的事件。此活動(dòng)將接口類(lèi)型和事件作為參數,當該事件被引發(fā)后,工作流會(huì )被喚醒,使操作繼續下去。
如果工作流必須對本地服務(wù)進(jìn)行回調,可以使用 CallExternalMethodActivity 并以接口和方法名為參數來(lái)實(shí)現。
通過(guò)這些活動(dòng),可以在宿主進(jìn)程中與運行中的工作流實(shí)現雙向通信,而通過(guò)在 WF 中使用“控制反轉”模式,避開(kāi)了工作流和本地服務(wù)之間的緊耦合。
然而,一旦跨出宿主進(jìn)程這一范圍,我們就必須允許交互由其他系統甚至是人來(lái)驅動(dòng)。為實(shí)現這一級別的交互,我們可以通過(guò)服務(wù)來(lái)分配交互操作,而這些服務(wù)進(jìn)而再由其他服務(wù)或用戶(hù)驅動(dòng)的應用程序來(lái)調用。而 WCF 就是能夠靈活構建這一消息傳遞功能的框架。
在這個(gè)與 WCF 集成的方案中,主要的優(yōu)點(diǎn)在于:
?
可以將服務(wù)實(shí)現與消息傳遞管道代碼相分離。
?
大大減少了與系統連接有關(guān)的代碼量和復雜程度。
?
增加了部署的靈活性。
?
可以利用從主機到客戶(hù)端的直接回調,使信息更新的速度更快、開(kāi)銷(xiāo)更低。
返回頁(yè)首集成工作檢查表
為了實(shí)現 WF 與 WCF 的集成,必須有一個(gè)將為使用者提供眾多接口點(diǎn)的服務(wù)接口,使用者可以在這些接口點(diǎn)啟動(dòng)運行中的工作流或與之交互。應圍繞業(yè)務(wù)流程與外部實(shí)體(例如這一流程所涉及的人員)進(jìn)行交互的這些接口點(diǎn)來(lái)建立服務(wù)模型。
圖 2.“開(kāi)支報告”方案中的交互點(diǎn)
為此,我們必須:
?
定義服務(wù)約定。
?
實(shí)現通過(guò)事件創(chuàng )建新工作流(或與現有工作流進(jìn)行交互)的服務(wù)操作。
?
將工作流運行時(shí)實(shí)例托管到服務(wù)宿主中。
除了簡(jiǎn)單地托管工作流之外,我們還可以利用 WCF 雙工信道將事件從工作流交回給作為使用方的客戶(hù)端。就“開(kāi)支報告”而言,這是非常有益的,因為該解決方案依靠客戶(hù)端進(jìn)行服務(wù)輪詢(xún)來(lái)實(shí)現定期的數據更新。但這些客戶(hù)端也可以直接從服務(wù)處獲得通知。
為此,我們必須:
?
定義一個(gè)回調約定。
?
使用將支持雙工信道的綁定。
返回頁(yè)首定義服務(wù)約定
Windows Communication Foundation (WCF) 要求聲明一份正式的約定來(lái)抽象地定義服務(wù)功能和數據交換。這份約定通過(guò)聲明接口以代碼的形式定義。
在設計業(yè)務(wù)服務(wù)時(shí),通常會(huì )使用“請求/響應”協(xié)作模式。使用這種模式時(shí),在所提供的約定中必須考慮三方面因素,即:
?
所發(fā)布的操作。這些操作是服務(wù)發(fā)布給其使用者的功能,是接口上的方法。
?
為每個(gè)請求和響應封裝結構化數據的消息。這些消息是每個(gè)方法的參數和返回類(lèi)型。在 WCF 術(shù)語(yǔ)中,它們通常是消息約定,而在更簡(jiǎn)單的情況中,它們是數據約定。
?
可通過(guò)服務(wù)進(jìn)行交換的核心業(yè)務(wù)實(shí)體的數據定義。這些定義構成消息的一部分。在 WCF 術(shù)語(yǔ)中,它們就是數據約定。
服務(wù)約定使用基于屬性的標記來(lái)定義,它首先定義提供操作的約定,然后再定義通過(guò)網(wǎng)絡(luò )發(fā)布的具體操作。
每個(gè)服務(wù)約定都以 [ServiceContract] 屬性明確標記。此屬性可通過(guò)下列參數進(jìn)行聲明:
?
Name??刂圃?WSDL <portType> 元素中聲明的約定名稱(chēng)
?
Namespace??刂圃?WSDL <portType> 元素中聲明的約定名稱(chēng)
?
SessionMode。指定約定是否需要支持會(huì )話(huà)的綁定
?
CallbackContract。指定用于客戶(hù)端回調的約定
?
ProtectionLevel。指定約定是否需要支持 ProtectionLevel 屬性的綁定,該屬性用于聲明加密和數字簽名需求
聲明操作
在約定定義之后,服務(wù)再由許多發(fā)布的操作構成。操作通過(guò) [OperationContract] 屬性標記被明確地加入約定之中。與 ServiceContract 一樣,OperationContract 也有許多用于控制與端點(diǎn)的綁定方式的參數。這些參數包括:
?
Action??刂朴糜谖ㄒ粯俗R此操作的名稱(chēng)。當某個(gè)端點(diǎn)接收到消息時(shí),調度程序會(huì )使用該控件和動(dòng)作來(lái)確定所要調用的方法。
?
IsOneWay。指示該操作將接受一個(gè)“請求”消息,但不會(huì )產(chǎn)生任何響應。這不同于簡(jiǎn)單地返回一個(gè)還將生成“結果”消息的 void 返回類(lèi)型。
?
ProtectionLevel。指定該操作的加密和簽名需求。
以下是代碼形式服務(wù)約定的示例。
[ServiceContract]public interface IExpenseService{[OperationContract]GetExpenseReportsResponse GetExpenseReports();[OperationContract]GetExpenseReportResponse GetExpenseReport(GetExpenseReportRequestgetExpenseReportRequest);}聲明消息和數據實(shí)體
在構建消息的模型時(shí),您可能希望采用類(lèi)的形式,來(lái)為每個(gè)將發(fā)送的消息定義負載或正文。這與在以 ASP.NET 構建 Web 服務(wù)時(shí)使用 WS Contract First (WSCF) 等工具來(lái)構建消息模型的方式十分相似。
在默認情況下,WCF 使用稱(chēng)為 DataContractSerializer 的序列化引擎來(lái)實(shí)現數據的序列化和反序列化(即與 XML 的來(lái)回轉換)。我們通過(guò)添加對 System.Runtime.Serialization 命名空間的引用來(lái)利用 DataContractSerializer,然后以 [DataContract] 屬性標記類(lèi),以 [DataMember] 標記要發(fā)布的成員。
[DataContract]public class GetExpenseReportsResponse{private List<ExpenseReport> reports;[DataMember]public List<ExpenseReport> Reports{get { return reports; }set { reports = value; }}}
消息中所使用的數據實(shí)體代表您業(yè)務(wù)領(lǐng)域中的實(shí)體。就像消息約定一樣,我們可以通過(guò) DataContractSerializer 和標記屬性來(lái)明確加入所分配的成員;或者,如果我們只想構建數據模型,可以使用公共字段的方法,并將類(lèi)標記為可序列化類(lèi)。
在本示例中,我們使用了數據約定的方法來(lái)標記消息傳遞。在實(shí)際的工作中,您常常會(huì )需要面對更為復雜的架構,需要在架構中使用屬性,以及需要使用 SOAP 標頭。WCF 支持定義以 [MessageContract] 屬性(可描述整個(gè) SOAP 封裝,而不僅僅是正文)標記的類(lèi),從而解決了這些尖銳的問(wèn)題。
有關(guān)數據約定和消息約定的詳細信息,請參閱本文結尾處更多信息部分所列的相應 MSDN 庫文章。
返回頁(yè)首托管工作流運行時(shí)
服務(wù)通常會(huì )支持將創(chuàng )建新服務(wù)類(lèi)型實(shí)例并在整個(gè)會(huì )話(huà)生命周期內維護該服務(wù)類(lèi)型實(shí)例的并行行為。要在這種情況下使用工作流,必須創(chuàng )建工作流運行時(shí)的一個(gè)實(shí)例,并在服務(wù)宿主實(shí)例的生命周期內(而不是基于每次調用)對其進(jìn)行維護。
對此,我們建議使用一個(gè)會(huì )在創(chuàng )建服務(wù)宿主后被激活的擴展類(lèi)。此擴展類(lèi)會(huì )創(chuàng )建和維護工作流運行時(shí)的全局實(shí)例,使每個(gè)單獨的服務(wù)實(shí)例都能夠訪(fǎng)問(wèn)它。
為在 ServiceHost 上實(shí)現擴展,應創(chuàng )建一個(gè)實(shí)現 IExtension<ServiceHostBase> 的類(lèi)。在此解決方案中,可以在 WcfExtensions 代碼項目下的 WfWcfExtension 類(lèi)中找到這樣一個(gè)示例。
我們需要實(shí)現的兩個(gè)方法是:Attach(當擴展附加到其父對象上時(shí)調用此方法)和 Detach(當卸載父對象時(shí)調用此方法)。
如下所示的 Attach 方法會(huì )創(chuàng )建一個(gè)新的 WorkflowRuntime 實(shí)例,并就所需服務(wù)將其進(jìn)行實(shí)例化。我們將此存儲在名為 workflowRuntime 的本地私有字段中。
void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner){workflowRuntime = new WorkflowRuntime(workflowServicesConfig);ExternalDataExchangeService exSvc = new ExternalDataExchangeService();workflowRuntime.AddService(exSvc);workflowRuntime.StartRuntime();}
如您所見(jiàn),工作流運行時(shí)的初始化還涉及在啟動(dòng)運行時(shí)之前將服務(wù)實(shí)例添加到其中。在構建解決方案時(shí),通常建議您在啟動(dòng)運行時(shí)之前添加所有的服務(wù)。但如果耦合是一個(gè)重要因素,您會(huì )發(fā)現采用后期綁定的方法更為明智。
在本示例中,作為 ExpenseService 類(lèi)中 SetUpWorkflowEnvironment 方法的一部分,我們在 WorkflowRuntime 啟動(dòng)后將 ExpenseLocalService 實(shí)例添加到 ExternalDataExchangeService 中。
以下所示的 Detach 方法通過(guò)調用 StopRuntime 來(lái)關(guān)閉運行時(shí)。
void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner){workflowRuntime.StopRuntime();}
由于 WorkflowRuntime 在服務(wù)宿主啟動(dòng)過(guò)程中創(chuàng )建并初始化,因此在服務(wù)調用執行前,任何現有的工作流都可以繼續工作。當服務(wù)宿主終止時(shí),工作流運行時(shí)會(huì )隨之徹底關(guān)閉。
注意 在托管工作流和構建長(cháng)期工作流模型時(shí),建議您使用工作流持久性服務(wù)(如 SqlWorkflowPersistenceService),這是一條基本的準則。這將提供一個(gè)實(shí)現狀態(tài)持久性的機制,即使重新啟動(dòng)應用程序或進(jìn)程也不會(huì )受到影響。
創(chuàng )建服務(wù)操作
要創(chuàng )建包含服務(wù)行為的類(lèi),必須實(shí)現一個(gè)或多個(gè)用于定義服務(wù)約定的接口。
public class ExpenseService :IExpenseService,IExpenseServiceClient,IExpenseServiceManager
為了與工作流相集成,我們的服務(wù)方法將不包含業(yè)務(wù)邏輯,而是包含一些代碼,用以控制事件或將事件交給將封裝業(yè)務(wù)流程的運行中工作流。
對于處理工作流的操作,我們將啟動(dòng)新的工作流,或者與現有的運行中工作流進(jìn)行交互。
要創(chuàng )建新的工作流實(shí)例,需要使用 WorkflowRuntime 來(lái)實(shí)例化所需工作流類(lèi)型的一個(gè)新實(shí)例。我們已在 ServiceHost 擴展類(lèi)中創(chuàng )建了一個(gè)這樣的實(shí)例。為了獲取對該實(shí)例的引用,我們必須使用 OperationContext 來(lái)找到自定義的擴展。
WfWcfExtension extension =OperationContext.Current.Host.Extensions.Find<WfWcfExtension>();workflowRuntime = extension.WorkflowRuntime;
OperationContext 是供我們訪(fǎng)問(wèn)服務(wù)方法擴展上下文的類(lèi)。正如您在之前的代碼中所看到的,它提供一個(gè)名為 Current 的單例,來(lái)為我們展示當前服務(wù)方法的上下文。我們調用 Host 屬性來(lái)將實(shí)例返回給所屬的 ServiceHost,然后根據其類(lèi)型找到擴展。
在獲得擴展實(shí)例的引用后,可以通過(guò)公共屬性返回 WorkflowRuntime,并用它來(lái)創(chuàng )建 SequentialWorkflow 的新實(shí)例。
Guid workflowInstanceId =submitExpenseReportRequest.Report.ExpenseReportId;Assembly asm = Assembly.Load("ExpenseWorkflows");Type workflowType = asm.GetType("ExpenseWorkflows.SequentialWorkflow");WorkflowInstance workflowInstance =workflowRuntime.CreateWorkflow(workflowType, null, workflowInstanceId);workflowInstance.Start();expenseLocalService.RaiseExpenseReportSubmittedEvent(workflowInstanceId, submitExpenseReportRequest.Report);
在上面的代碼中,我們基于預定義的類(lèi)型創(chuàng )建了新的工作流實(shí)例。雖然這也可以通過(guò)直接的類(lèi)型實(shí)例化來(lái)實(shí)現,但上述的方式告訴我們,其實(shí)可以在運行時(shí)基于動(dòng)態(tài)規則(而不是通過(guò)強類(lèi)型化綁定)來(lái)靈活地創(chuàng )建工作流。
最后一行代碼會(huì )引發(fā)由工作流中第一個(gè) HandleExternalEventActivity 處理的事件,標志著(zhù)工作流的開(kāi)始。我們通過(guò) ExpenseLocalService 類(lèi)的實(shí)例來(lái)引發(fā)這一事件。在本示例中,ExpenseLocalService 會(huì )啟動(dòng)新的工作流或將事件交給現有工作流,從而與工作流進(jìn)行交互。我們將此類(lèi)用作封裝業(yè)務(wù)流程的機制。在內部,我們使用 WF 來(lái)實(shí)現這一機制。
圖 3. 工作流始于 HandleExternalEventActivity。
我們將要應對的另一類(lèi)情況是如何回調到現有工作流中并引發(fā)事件。我們必須將事件交給將促使現有工作流接收事件并繼續進(jìn)行處理的工作流引擎。
在“開(kāi)支報告”流程中引發(fā)事件的一個(gè)示例就是在需要管理人員進(jìn)行審批之時(shí)發(fā)生的。工作流將對 RequestManagerApproval 調用外部方法,用以向管理人員發(fā)出警報,告訴他們必須批準或拒絕新的開(kāi)支報告。
工作流中包含在一個(gè)可能事件發(fā)生前一直處于阻塞狀態(tài)的 ListenActivity。在本例中,我們會(huì )接收到一個(gè)事件,指示管理人員已經(jīng)審核該報告,或者已經(jīng)超過(guò) DelayActivity 的時(shí)間限制。
圖 4. ManagerApproval 自定義活動(dòng)流程
Guid workflowInstanceId =submitReviewedExpenseReportRequest.Report.ExpenseReportId;ExpenseReportReviewedEventArgs e =new ExpenseReportReviewedEventArgs(workflowInstanceId, report, review);if (ExpenseReportReviewed != null){ExpenseReportReviewed(null, e);}
管理人員使用 ManagerApplication 審核報告時(shí),會(huì )對宿主進(jìn)行服務(wù)回調,來(lái)調用引發(fā) ExpenseReportReviewed 事件的 SubmitReviewedExpenseReport 方法。
引發(fā)交給工作流中 HandleExternalEventActivity 的事件時(shí),必須知道所處理工作流實(shí)例的 GUID,使事件能夠得以路由。
每個(gè)事件都以 EventArgs 引發(fā),使我們能夠通過(guò)事件模型將數據傳遞回工作流。在本例中,我們可以傳遞詳細的當前報表狀態(tài)信息和審核活動(dòng)上下文數據信息。
在工作流中,事件通過(guò) HandleExternalEventActivity 的屬性自動(dòng)綁定到工作流。
圖 5. 將 HandleExternalEventActivity 綁定到 IExpenseLocalService 接口。
首先指定必須以 [ExternalDataExchange] 屬性標記的接口類(lèi)型,然后指定該接口上 HandleExternalEventActivity 將訂閱的事件。
事件參數必須從 ExternalDataEventArgs 類(lèi)派生而來(lái)。這至少意味著(zhù)每個(gè)事件都會(huì )包含上下文(例如工作流的 InstanceId)。然后,工作流運行時(shí)負責將事件路由給正確的工作流實(shí)例,以使工作流繼續進(jìn)行。如果使用持久性服務(wù),運行時(shí)還會(huì )在整個(gè)執行期間管理工作流所有運行狀態(tài)的水化和再水化。
托管服務(wù)
要托管 WCF 服務(wù),必須在 ServiceHost 容器內運行。
為了了解如何使用 WCF 實(shí)現托管,首先讓我們來(lái)了解一下幾種可用的備選方案:
?
對于標準的 Windows 進(jìn)程,可以手動(dòng)創(chuàng )建并打開(kāi) ServiceHost 實(shí)例。
?
在通過(guò) Microsoft Internet Information Services (IIS) 6.0 托管 Web 端點(diǎn)(Web 服務(wù))時(shí),使用 System.ServiceModel 命名空間下提供的自定義 HttpHandler。
?
在 IIS 7 下進(jìn)行托管時(shí),可以使用 Windows Activation Service (WAS) 來(lái)托管端點(diǎn)。
構建 Web 服務(wù)時(shí),您通常會(huì )選擇使用 Internet Information Services 進(jìn)行托管。而在構建將用作后臺程序的單個(gè)實(shí)例端口時(shí),您通常會(huì )選擇通過(guò) Windows 服務(wù)進(jìn)行托管。
在本示例中,我們在一個(gè) Windows 控制臺應用程序中托管主要的服務(wù)實(shí)例,托管方式與 Windows 服務(wù)的托管方式類(lèi)似。
為了部署服務(wù),我們必須創(chuàng )建 ServiceHost 類(lèi)的一個(gè)實(shí)例,并針對要發(fā)布的每個(gè)服務(wù)類(lèi)型開(kāi)放其端點(diǎn)。ServiceHost 將許多參數作為其構造函數的一部分;但主要參數是 Type 參數或實(shí)現 ServiceContract 的類(lèi)的實(shí)例。
?
如果要采用 PerCall 或 PerSession 實(shí)例化,請使用 Type。
?
如果采用 Single 實(shí)例化,請使用單個(gè)實(shí)例。
有關(guān) WCF 中實(shí)例化與并發(fā)性的詳細信息,請參閱 MSDN 庫中的
Sessions, Instancing, and Concurrency。
宿主建立起來(lái)后,會(huì )分析所有可用的配置(在下面的配置部署部分做詳細介紹)并將其與任何明確添加的配置合并,來(lái)確定可用的端點(diǎn)并針對發(fā)布開(kāi)放那些端點(diǎn)。宿主接收到從客戶(hù)端發(fā)來(lái)的調用后,會(huì )在新的后臺工作線(xiàn)程上處理請求,并按消息中 SOAP 約定名稱(chēng)和動(dòng)作的指示,將其路由給相應的服務(wù)操作。
using (ServiceHost serviceHost = new ServiceHost(new ExpenseService())){WfWcfExtension wfWcfExtension =new WfWcfExtension("WorkflowRuntimeConfig");serviceHost.Extensions.Add(wfWcfExtension);serviceHost.Open();// 在此處阻止該進(jìn)程,例如 Console.ReadLine();serviceHost.Close();}
配置 ServiceHost 時(shí),必須首先為連接開(kāi)放端點(diǎn)。為此,您可以在調用 .Open() 之前與宿主對象進(jìn)行交互,如前面代碼所示。建議您利用作用域在使用前對 ServiceHost 進(jìn)行處置,并建議您在該作用域結束時(shí)明確地調用 Close() 來(lái)徹底關(guān)閉所有活動(dòng)的連接和端點(diǎn)。
返回頁(yè)首配置部署
WCF 提供了一種機制,允許通過(guò) XML 配置來(lái)配置端點(diǎn),從而將部署問(wèn)題從實(shí)現工作中分離出來(lái)。這使得管理員無(wú)需重新部署代碼即可修改服務(wù)策略。
每個(gè)服務(wù)在一個(gè)或多個(gè)端點(diǎn)上發(fā)布。一個(gè)端點(diǎn)就是一個(gè)可尋址的連接點(diǎn),客戶(hù)端可對其使用服務(wù)。在 WCF 中,每個(gè)端點(diǎn)都以三個(gè)屬性聲明,這三個(gè)屬性就是人們所熟知的 WCF 的 ABC。
它們是“地址”(A)、“綁定”(B) 和“約定”(C)。
地址:此端點(diǎn)的唯一可尋址位置。通常情況下,地址是為您提供絕對地址的 URI,服務(wù)在該地址處偵聽(tīng)請求,例如:http://myhost/myservice 或 net.tcp://myhost:400/myservice
綁定:用于規定服務(wù)與其使用者之間通信協(xié)議的策略。綁定指定了幾個(gè)方面的要素,例如所使用的傳輸類(lèi)型、消息的編碼方式以及數據的序列化方式。WCF 附帶了許多用于支持最常見(jiàn)情況的現成綁定。
約定:通過(guò)接口以代碼形式定義的、要發(fā)布的操作和數據。
要配置服務(wù),我們必須對聲明服務(wù)的配置進(jìn)行聲明,并為服務(wù)配置任意數量的端點(diǎn)。由于服務(wù)可能正在實(shí)現任意多個(gè)約定,因此這還將影響到需要發(fā)布的端點(diǎn)數。
以下是一個(gè)配置示例。
<services><service name="ExpenseServices.ExpenseService"><endpointaddress="http://localhost:8081/ExpenseService/Manager"binding="wsHttpBinding"contract="ExpenseContracts.IExpenseServiceManager" /><endpointaddress="http://localhost:8081/ExpenseService/Client"binding="wsDualHttpBinding"contract="ExpenseContracts.IExpenseServiceClient" /></service></services>
在此配置示例中,我們?yōu)?ExpenseServices.ExpenseService 類(lèi)型的服務(wù)聲明配置。這樣,運行時(shí)就可以在我們根據此類(lèi)型實(shí)例化新的 ServiceHost 時(shí)找到該配置。有關(guān)綁定的詳細信息,請參閱 MSDN 庫中的
WCF Bindings。
返回頁(yè)首使用服務(wù)
通過(guò) ChannelFactory 類(lèi),可以利用 WCF 來(lái)使用服務(wù)。ChannelFactory 通過(guò)工廠(chǎng)模式為我們提供連接到配置中指定端點(diǎn)的服務(wù)約定代理實(shí)例。我們可以使用運行時(shí)信息(例如用于消息加密的安全憑據和證書(shū))配置工廠(chǎng),或者動(dòng)態(tài)地確定端點(diǎn)信息。
private IExpenseServiceManager CreateChannelExpenseServiceManager(){ChannelFactory<IExpenseServiceManager> factory = newChannelFactory<IExpenseServiceManager>("ExpenseServiceManager");IExpenseServiceManager proxy = factory.CreateChannel();return proxy;}
如您所見(jiàn),我們首先創(chuàng )建了一個(gè)工廠(chǎng)實(shí)例,該實(shí)例對服務(wù)約定使用泛型參數,以構造出將僅返回所需約定實(shí)例的更精確的工廠(chǎng)。我們還指定了用于確定端點(diǎn)所用配置的參數。在本例中,我們使用名為 ExpenseServiceManager 的端點(diǎn)配置,它就是我們應用程序配置文件中的配置。
<system.serviceModel><client><endpoint name="ExpenseServiceManager"address="http://localhost:8081/ExpenseService/Manager"binding="wsHttpBinding"contract="ExpenseContracts.IExpenseServiceManager" /></client></system.serviceModel>
您會(huì )發(fā)現端點(diǎn)定義與在宿主配置中聲明的定義完全相符。通常,只有在因網(wǎng)絡(luò )配置而造成客戶(hù)端與服務(wù)器的地址不同時(shí),或者在實(shí)現自定義行為時(shí),才會(huì )出現配置的差異。
如果安裝了 Windows SDK,您會(huì )發(fā)現一個(gè)用于自動(dòng)創(chuàng )建代理類(lèi)和端點(diǎn)配置的工具,您可以將其集成到您的解決方案中。目標服務(wù)必須通過(guò) WSDL 或 WS-MetadataExchange 發(fā)布其元數據的說(shuō)明,才能利用此工具。
返回頁(yè)首配置雙工信道
到現在為止,我們一直在假設我們的通信流采用的是請求/響應協(xié)作模式,消息由使用者發(fā)出并由服務(wù)做出應答。WCF 支持許多備選的消息流,例如單向(“即發(fā)即棄”模式)或雙向雙工通信。如果處理每一方都可以啟動(dòng)會(huì )話(huà)的消息流,則需要使用雙工或雙向信道。對于那些連接更為緊密且支持沿任一方向發(fā)送數據的系統而言,雙工信道非常有效。例如,如果要提供始于事件處理過(guò)程的回調,雙工信道將非常有用。
實(shí)現客戶(hù)端回調
在 WCF 中,客戶(hù)端回調通過(guò)一個(gè)稱(chēng)為 CallbackContracts 的概念來(lái)實(shí)現。對于所發(fā)布的約定,我們可以指定另一個(gè)約定來(lái)定義客戶(hù)端將發(fā)布的、可由服務(wù)上運行的代碼回調的操作。
要聲明 CallbackContract,應在發(fā)出回調的服務(wù)約定中指定接口類(lèi)型。
[ServiceContract(CallbackContract =typeof(IExpenseServiceClientCallback))]
您還需要使用支持雙工信道的綁定,如 netTcpBinding 或 wsDualHttpBinding?;?TCP 的雙工通信通過(guò)在整個(gè)消息交換過(guò)程中所建立和維護的雙向連接來(lái)實(shí)現。而基于 HTTP 的雙工通信則通過(guò)對客戶(hù)端偵聽(tīng)程序的回調來(lái)實(shí)現。由于客戶(hù)端可能不知道其返回路徑,或者您可能希望通過(guò)配置對其進(jìn)行強定義,因此我們可以使用自定義綁定配置來(lái)聲明一個(gè)替代的 clientBaseAddress。
<endpoint binding="wsDualHttpBinding"bindingConfiguration="AlternativeClientCallback"/><bindings><wsDualHttpBinding><binding name="AlternativeClientCallback"clientBaseAddress="http://localhost:8082/ExpenseService/ClientCallback"/></wsDualHttpBinding></bindings>在客戶(hù)端實(shí)現回調
實(shí)現回調約定的方式與實(shí)現服務(wù)約定的方式是完全相同的。我們必須提供所定義接口的實(shí)現。
class CallbackHandler : IExpenseServiceClientCallback{public void ExpenseReportReviewed(ExpenseReportReviewedRequest expenseReportReviewedRequest){// 在此實(shí)現客戶(hù)端邏輯以對回調作出響應。}}
為了使宿主回調到 CallbackHandler 類(lèi)的實(shí)例,建立的客戶(hù)端信道必須能夠知曉連接的雙工特性。
如之前所述,首先使用支持雙工信道的綁定。其次,在初始化與服務(wù)端點(diǎn)的連接時(shí),使用名為 DuplexChannelFactory 的 ChannelFactory 子類(lèi)版本,它將創(chuàng )建與服務(wù)的雙工連接。
private IExpenseServiceClient CreateChannelExpenseServiceClient(){InstanceContext context = new InstanceContext(new CallbackHandler());DuplexChannelFactory<IExpenseServiceClient> factory =new DuplexChannelFactory<IExpenseServiceClient>(context,"ExpenseServiceClient");IExpenseServiceClient proxy = factory.CreateChannel();return proxy;}
使用 DuplexChannelFactory 的主要不同之處在于,要先初始化 CallbackHandler 類(lèi)的實(shí)例,并將其傳遞給工廠(chǎng)構造函數,以便初始化回調所使用的上下文。
實(shí)現宿主的回調
從宿主的角度來(lái)看,我們可以通過(guò)在 IExpenseServiceClient 約定中定義的回調信道來(lái)獲取對面向客戶(hù)端的回調的一個(gè)引用。
[ServiceContract(CallbackContract =typeof(IExpenseServiceClientCallback))]public interface IExpenseServiceClient : IExpenseService
CallbackContract 屬性聲明用于定義宿主回調約定的接口。
為了進(jìn)行回調,我們通過(guò)調用 OperationContext.Current.GetCallbackChannel 來(lái)獲取回調約定的引用,如下所示。
IExpenseServiceClientCallback callback =OperationContext.Current.GetCallbackChannel<IExpenseServiceClientCallback>();callback.ExpenseReportReviewed(newExpenseReportReviewedRequest(e.Report));
在獲得對回調信道的引用后,我們即可正常地進(jìn)行調用。
返回頁(yè)首結束語(yǔ)
Windows Workflow Foundation 提供了一個(gè)用于定義工作流的通用框架,以及一個(gè)用于托管運行中工作流并與之進(jìn)行交互的、穩健的運行時(shí)引擎。
Windows Communication Foundation 提供了用于構建互聯(lián)系統的通用框架,并為開(kāi)發(fā)人員提供了一致的 API 和豐富的功能集,來(lái)定義通信方式。
您可以將這兩種框架結合起來(lái),為在所處環(huán)境中構建和部署分布式業(yè)務(wù)流程提供靈活而全面的應用程序平臺。WF 允許您為業(yè)務(wù)邏輯和流程建模并進(jìn)行封裝,而 WCF 又為您提供了具有多種系統分布方式選擇的消息傳遞基礎結構。
以下是您在設計服務(wù)時(shí)需要牢記的幾條原則:
?
應通過(guò)使用持久性服務(wù)來(lái)支持長(cháng)時(shí)間運行的工作流。
?
服務(wù)操作可以通過(guò)運行工作流、通過(guò)引發(fā)事件進(jìn)行交互。應將工作流設計為在需要介入時(shí)引發(fā)事件,在與外部(例如,外部服務(wù)或人員)進(jìn)行交互時(shí)響應事件。
?
工作流將異步執行服務(wù)調用,因此考慮自服務(wù)返回的數據及當時(shí)數據所處的狀態(tài),可有針對性地進(jìn)行設計。如果要使用同步方法,可以利用 ManualWorkflowSchedulerService 類(lèi)對工作流的執行進(jìn)行手動(dòng)調度。
返回頁(yè)首更多信息
?
Sessions, Instancing, and Concurrency ?
WCF Bindings ?
Using Data Contracts ?
Using Message Contracts ?
.NET Framework 3.0 社區站點(diǎn) ?
Windows Workflow Foundation 論壇 返回頁(yè)首致謝
特此對以下付出過(guò)辛勤勞動(dòng)的貢獻者和審核人員表示萬(wàn)分的感謝:
?
Christian Weyer, thinktecture
?
Paul Andrew, Microsoft Corporation
?
Khalid Aggag, Microsoft Corporation
?
Patrice Manac‘h, Microsoft Corporation
作者簡(jiǎn)介
Jeremy Boyd 是 Intergen 公司(總部位于新西蘭的解決方案供應商,是 Microsoft 金牌認證合作伙伴)的高級技術(shù)顧問(wèn),也是 MSDN 新西蘭區區域總監。在過(guò)去的 12 個(gè)月里,Jeremy 與客戶(hù)積極合作,致力于幫助實(shí)現基于 WF 和 WCF 的解決方案,并通過(guò)他的
博客幫助其他開(kāi)發(fā)人員了解這些技術(shù)所帶來(lái)的益處。