日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Windows Workflow Foundation 與 Windows Communication Foundation 集成(轉(zhuǎn)于MSDN)

 快樂學(xué)習(xí) 2007-02-14
?

Windows Workflow Foundation

?

Windows Communication Foundation

?

Microsoft Visual Studio 2005

摘要:本文簡要介紹如何在通過 Windows Communication Foundation (WCF) 創(chuàng)建的服務(wù)內(nèi)托管用 Windows Workflow Foundation (WF) 所構(gòu)建的工作流。本文還將介紹如何利用 WCF 所提供的眾多功能中的幾個(gè),來通過雙工信道簡化客戶端事件回調(diào)。(本文還包含指向英文網(wǎng)頁的鏈接。)

*
本頁內(nèi)容
簡介 簡介
開支報(bào)告示例 開支報(bào)告示例
集成工作檢查表 集成工作檢查表
定義服務(wù)約定 定義服務(wù)約定
托管工作流運(yùn)行時(shí) 托管工作流運(yùn)行時(shí)
配置部署 配置部署
使用服務(wù) 使用服務(wù)
配置雙工信道 配置雙工信道
結(jié)束語 結(jié)束語
更多信息 更多信息
致謝 致謝

單擊此處下載“Windows Workflow Foundation 示例:WF 與 WCF 集成”。

簡介

隨著 Windows Workflow Foundation (WF) 的問世,Microsoft 逐步將各種工作流功能引入了 .NET 開發(fā)人員平臺(tái)。這些功能使開發(fā)人員能夠構(gòu)建用于滿足各種應(yīng)用需求的工作流,從簡單的順序工作流到需要復(fù)雜的人員交互的復(fù)雜狀態(tài)機(jī)工作流。

與此同時(shí),業(yè)務(wù)能力越來越多地通過封裝的服務(wù)端點(diǎn)展現(xiàn)出來,這樣就可以重用和組合業(yè)務(wù)功能和業(yè)務(wù)流程,使面向服務(wù)的體系架構(gòu)更加完善。Windows Communication Foundation (WCF) 提供了統(tǒng)一的開發(fā)人員 API、穩(wěn)健的托管運(yùn)行時(shí)和靈活的配置驅(qū)動(dòng)解決方案來幫助進(jìn)行部署,進(jìn)而幫助開發(fā)人員通過各種功能來輕松開發(fā)互聯(lián)系統(tǒng)。

本文在結(jié)尾處列出了一些可用來進(jìn)一步了解 WF 和 WCF 的附加資源。

開支報(bào)告示例

本文的代碼示例基于為員工報(bào)銷申請(qǐng)?zhí)峤慌c審批標(biāo)準(zhǔn)業(yè)務(wù)流程建立模型的“開支報(bào)告”工作流示例。我們?cè)谠纠A(chǔ)上進(jìn)行了更新,用以說明如何利用 WCF 和 .NET 3.0 Framework 來更有效地托管這一業(yè)務(wù)環(huán)節(jié)。

在發(fā)行的第一版“開支報(bào)告”示例中,使用 .NET 遠(yuǎn)程調(diào)用來提供客戶端應(yīng)用程序與包含工作流運(yùn)行時(shí)實(shí)例的宿主應(yīng)用程序之間的通信。

而經(jīng)我們重構(gòu)的開支報(bào)告示例在實(shí)現(xiàn)時(shí)使用 WCF 來執(zhí)行客戶端和服務(wù)端之間的通信。此外,該解決方案還實(shí)現(xiàn)了邏輯上的結(jié)構(gòu)化,從而將其中的各種問題分離出來。

.

圖 1. 重構(gòu)后解決方案的結(jié)構(gòu)

了解消息在業(yè)務(wù)流程上下文中的使用方式非常重要,只有這樣您才能將它們?nèi)谌朐O(shè)計(jì)之中。在“開支報(bào)告”的生命周期中,存在幾個(gè)交互點(diǎn)。我們來簡單分析一下:

?

這一流程涉及到三方:客戶端、管理人員和開支報(bào)告宿主系統(tǒng)。

?

這一流程從客戶端提交新的報(bào)銷申請(qǐng)開始。

?

我們使用一種規(guī)則策略來確定報(bào)銷申請(qǐng)是否可以自動(dòng)審批。

?

如果報(bào)銷申請(qǐng)不能自動(dòng)審批,則需要管理人員審批該報(bào)告。管理人員要么需要自行檢查是否有待審批的新報(bào)告,要么需要在有待審批的新報(bào)告時(shí)獲得通知。

?

如果管理人員在靈活的延遲時(shí)間內(nèi)未進(jìn)行審批,該流程會(huì)自動(dòng)拒絕此報(bào)銷申請(qǐng)。

?

報(bào)銷申請(qǐng)被復(fù)審?fù)戤吅?,客戶端和管理人員必須被及時(shí)告知結(jié)果。

通過 WF,我們可以利用該框架所提供的標(biāo)準(zhǔn)活動(dòng)來為這一流程建模。我們可以使用 DelayActivity 來管理在一段時(shí)間后觸發(fā)的事件,可以使用規(guī)則引擎和 PolicyActivity 來管理一套靈活的規(guī)則,通過詢問這些規(guī)則可以獲得結(jié)果。

由于這是一個(gè)面向人員的流程,因此我們必須與最終用戶進(jìn)行交互,并將該交互交回到工作流中。WF 提供了“本地服務(wù)”、HandleExternalEventActivity 和 CallExternalMethodActivity,從而為實(shí)現(xiàn)宿主和工作流之間的通信提供了全面的編程模型。

由于這對(duì)于構(gòu)建交互式工作流而言是一個(gè)重要的概念,因此讓我們快來了解一下它是如何被設(shè)計(jì)到 WF 中的。

為了在 WF 中建立用戶交互的模型,我們必須設(shè)計(jì)用于提供許多事件和方法的約定。工作流和宿主進(jìn)程都應(yīng)理解這一約定。所構(gòu)建的約定/接口必須以 [ExternalDataExchange()] 屬性來標(biāo)記,這一屬性會(huì)將其標(biāo)識(shí)為專用于進(jìn)行工作流數(shù)據(jù)交換。在我們所列舉的示例中,工作流使用 IExpenseLocalService 接口。

然后我們訂閱一個(gè)與工作流運(yùn)行時(shí)實(shí)現(xiàn)該接口的類(稱為本地服務(wù))。工作流活動(dòng)可以注冊(cè)到事件或者使用接口類型上定義的方法,并將綁定到我們已經(jīng)注冊(cè)的本地服務(wù)。這采用的是一種稱為“控制反轉(zhuǎn)”的模式,這種模式消除了工作流與具體類型本地服務(wù)之間的緊耦合。在我們所列舉的示例中,ExpenseLocalService 類實(shí)現(xiàn)了 IExpenseLocalService 約定。

工作流在第一次運(yùn)行時(shí),可以獲得一個(gè)用于執(zhí)行操作的初始數(shù)據(jù)包。當(dāng)工作流到達(dá)需要外部交互的點(diǎn)后,我們可以引發(fā)一個(gè)能夠在工作流中綁定到 HandleExternalEventActivity 的事件。此活動(dòng)將接口類型和事件作為參數(shù),當(dāng)該事件被引發(fā)后,工作流會(huì)被喚醒,使操作繼續(xù)下去。

如果工作流必須對(duì)本地服務(wù)進(jìn)行回調(diào),可以使用 CallExternalMethodActivity 并以接口和方法名為參數(shù)來實(shí)現(xiàn)。

通過這些活動(dòng),可以在宿主進(jìn)程中與運(yùn)行中的工作流實(shí)現(xiàn)雙向通信,而通過在 WF 中使用“控制反轉(zhuǎn)”模式,避開了工作流和本地服務(wù)之間的緊耦合。

然而,一旦跨出宿主進(jìn)程這一范圍,我們就必須允許交互由其他系統(tǒng)甚至是人來驅(qū)動(dòng)。為實(shí)現(xiàn)這一級(jí)別的交互,我們可以通過服務(wù)來分配交互操作,而這些服務(wù)進(jìn)而再由其他服務(wù)或用戶驅(qū)動(dòng)的應(yīng)用程序來調(diào)用。而 WCF 就是能夠靈活構(gòu)建這一消息傳遞功能的框架。

在這個(gè)與 WCF 集成的方案中,主要的優(yōu)點(diǎn)在于:

?

可以將服務(wù)實(shí)現(xiàn)與消息傳遞管道代碼相分離。

?

大大減少了與系統(tǒng)連接有關(guān)的代碼量和復(fù)雜程度。

?

增加了部署的靈活性。

?

可以利用從主機(jī)到客戶端的直接回調(diào),使信息更新的速度更快、開銷更低。

集成工作檢查表

為了實(shí)現(xiàn) WF 與 WCF 的集成,必須有一個(gè)將為使用者提供眾多接口點(diǎn)的服務(wù)接口,使用者可以在這些接口點(diǎn)啟動(dòng)運(yùn)行中的工作流或與之交互。應(yīng)圍繞業(yè)務(wù)流程與外部實(shí)體(例如這一流程所涉及的人員)進(jìn)行交互的這些接口點(diǎn)來建立服務(wù)模型。

.

圖 2.“開支報(bào)告”方案中的交互點(diǎn)

為此,我們必須:

?

定義服務(wù)約定。

?

實(shí)現(xiàn)通過事件創(chuàng)建新工作流(或與現(xiàn)有工作流進(jìn)行交互)的服務(wù)操作。

?

將工作流運(yùn)行時(shí)實(shí)例托管到服務(wù)宿主中。

除了簡單地托管工作流之外,我們還可以利用 WCF 雙工信道將事件從工作流交回給作為使用方的客戶端。就“開支報(bào)告”而言,這是非常有益的,因?yàn)樵摻鉀Q方案依靠客戶端進(jìn)行服務(wù)輪詢來實(shí)現(xiàn)定期的數(shù)據(jù)更新。但這些客戶端也可以直接從服務(wù)處獲得通知。

為此,我們必須:

?

定義一個(gè)回調(diào)約定。

?

使用將支持雙工信道的綁定。

定義服務(wù)約定

Windows Communication Foundation (WCF) 要求聲明一份正式的約定來抽象地定義服務(wù)功能和數(shù)據(jù)交換。這份約定通過聲明接口以代碼的形式定義。

在設(shè)計(jì)業(yè)務(wù)服務(wù)時(shí),通常會(huì)使用“請(qǐng)求/響應(yīng)”協(xié)作模式。使用這種模式時(shí),在所提供的約定中必須考慮三方面因素,即:

?

所發(fā)布的操作。這些操作是服務(wù)發(fā)布給其使用者的功能,是接口上的方法。

?

為每個(gè)請(qǐng)求和響應(yīng)封裝結(jié)構(gòu)化數(shù)據(jù)的消息。這些消息是每個(gè)方法的參數(shù)和返回類型。在 WCF 術(shù)語中,它們通常是消息約定,而在更簡單的情況中,它們是數(shù)據(jù)約定。

?

可通過服務(wù)進(jìn)行交換的核心業(yè)務(wù)實(shí)體的數(shù)據(jù)定義。這些定義構(gòu)成消息的一部分。在 WCF 術(shù)語中,它們就是數(shù)據(jù)約定。

服務(wù)約定使用基于屬性的標(biāo)記來定義,它首先定義提供操作的約定,然后再定義通過網(wǎng)絡(luò)發(fā)布的具體操作。

每個(gè)服務(wù)約定都以 [ServiceContract] 屬性明確標(biāo)記。此屬性可通過下列參數(shù)進(jìn)行聲明:

?

Name??刂圃?WSDL <portType> 元素中聲明的約定名稱

?

Namespace??刂圃?WSDL <portType> 元素中聲明的約定名稱

?

SessionMode。指定約定是否需要支持會(huì)話的綁定

?

CallbackContract。指定用于客戶端回調(diào)的約定

?

ProtectionLevel。指定約定是否需要支持 ProtectionLevel 屬性的綁定,該屬性用于聲明加密和數(shù)字簽名需求

聲明操作

在約定定義之后,服務(wù)再由許多發(fā)布的操作構(gòu)成。操作通過 [OperationContract] 屬性標(biāo)記被明確地加入約定之中。與 ServiceContract 一樣,OperationContract 也有許多用于控制與端點(diǎn)的綁定方式的參數(shù)。這些參數(shù)包括:

?

Action??刂朴糜谖ㄒ粯?biāo)識(shí)此操作的名稱。當(dāng)某個(gè)端點(diǎn)接收到消息時(shí),調(diào)度程序會(huì)使用該控件和動(dòng)作來確定所要調(diào)用的方法。

?

IsOneWay。指示該操作將接受一個(gè)“請(qǐng)求”消息,但不會(huì)產(chǎn)生任何響應(yīng)。這不同于簡單地返回一個(gè)還將生成“結(jié)果”消息的 void 返回類型。

?

ProtectionLevel。指定該操作的加密和簽名需求。

以下是代碼形式服務(wù)約定的示例。

[ServiceContract]
public interface IExpenseService
{
[OperationContract]
GetExpenseReportsResponse GetExpenseReports();
[OperationContract]
GetExpenseReportResponse GetExpenseReport(GetExpenseReportRequest
getExpenseReportRequest);
}

聲明消息和數(shù)據(jù)實(shí)體

在構(gòu)建消息的模型時(shí),您可能希望采用類的形式,來為每個(gè)將發(fā)送的消息定義負(fù)載或正文。這與在以 ASP.NET 構(gòu)建 Web 服務(wù)時(shí)使用 WS Contract First (WSCF) 等工具來構(gòu)建消息模型的方式十分相似。

在默認(rèn)情況下,WCF 使用稱為 DataContractSerializer 的序列化引擎來實(shí)現(xiàn)數(shù)據(jù)的序列化和反序列化(即與 XML 的來回轉(zhuǎn)換)。我們通過添加對(duì) System.Runtime.Serialization 命名空間的引用來利用 DataContractSerializer,然后以 [DataContract] 屬性標(biāo)記類,以 [DataMember] 標(biāo)記要發(fā)布的成員。

[DataContract]
public class GetExpenseReportsResponse
{
private List<ExpenseReport> reports;
[DataMember]
public List<ExpenseReport> Reports
{
get { return reports; }
set { reports = value; }
}
}

消息中所使用的數(shù)據(jù)實(shí)體代表您業(yè)務(wù)領(lǐng)域中的實(shí)體。就像消息約定一樣,我們可以通過 DataContractSerializer 和標(biāo)記屬性來明確加入所分配的成員;或者,如果我們只想構(gòu)建數(shù)據(jù)模型,可以使用公共字段的方法,并將類標(biāo)記為可序列化類。

在本示例中,我們使用了數(shù)據(jù)約定的方法來標(biāo)記消息傳遞。在實(shí)際的工作中,您常常會(huì)需要面對(duì)更為復(fù)雜的架構(gòu),需要在架構(gòu)中使用屬性,以及需要使用 SOAP 標(biāo)頭。WCF 支持定義以 [MessageContract] 屬性(可描述整個(gè) SOAP 封裝,而不僅僅是正文)標(biāo)記的類,從而解決了這些尖銳的問題。

有關(guān)數(shù)據(jù)約定和消息約定的詳細(xì)信息,請(qǐng)參閱本文結(jié)尾處更多信息部分所列的相應(yīng) MSDN 庫文章。

托管工作流運(yùn)行時(shí)

服務(wù)通常會(huì)支持將創(chuàng)建新服務(wù)類型實(shí)例并在整個(gè)會(huì)話生命周期內(nèi)維護(hù)該服務(wù)類型實(shí)例的并行行為。要在這種情況下使用工作流,必須創(chuàng)建工作流運(yùn)行時(shí)的一個(gè)實(shí)例,并在服務(wù)宿主實(shí)例的生命周期內(nèi)(而不是基于每次調(diào)用)對(duì)其進(jìn)行維護(hù)。

對(duì)此,我們建議使用一個(gè)會(huì)在創(chuàng)建服務(wù)宿主后被激活的擴(kuò)展類。此擴(kuò)展類會(huì)創(chuàng)建和維護(hù)工作流運(yùn)行時(shí)的全局實(shí)例,使每個(gè)單獨(dú)的服務(wù)實(shí)例都能夠訪問它。

為在 ServiceHost 上實(shí)現(xiàn)擴(kuò)展,應(yīng)創(chuàng)建一個(gè)實(shí)現(xiàn) IExtension<ServiceHostBase> 的類。在此解決方案中,可以在 WcfExtensions 代碼項(xiàng)目下的 WfWcfExtension 類中找到這樣一個(gè)示例。

我們需要實(shí)現(xiàn)的兩個(gè)方法是:Attach(當(dāng)擴(kuò)展附加到其父對(duì)象上時(shí)調(diào)用此方法)和 Detach(當(dāng)卸載父對(duì)象時(shí)調(diào)用此方法)。

如下所示的 Attach 方法會(huì)創(chuàng)建一個(gè)新的 WorkflowRuntime 實(shí)例,并就所需服務(wù)將其進(jìn)行實(shí)例化。我們將此存儲(chǔ)在名為 workflowRuntime 的本地私有字段中。

void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
{
workflowRuntime = new WorkflowRuntime(workflowServicesConfig);
ExternalDataExchangeService exSvc = new ExternalDataExchangeService();
workflowRuntime.AddService(exSvc);
workflowRuntime.StartRuntime();
}

如您所見,工作流運(yùn)行時(shí)的初始化還涉及在啟動(dòng)運(yùn)行時(shí)之前將服務(wù)實(shí)例添加到其中。在構(gòu)建解決方案時(shí),通常建議您在啟動(dòng)運(yùn)行時(shí)之前添加所有的服務(wù)。但如果耦合是一個(gè)重要因素,您會(huì)發(fā)現(xiàn)采用后期綁定的方法更為明智。

在本示例中,作為 ExpenseService 類中 SetUpWorkflowEnvironment 方法的一部分,我們?cè)?WorkflowRuntime 啟動(dòng)后將 ExpenseLocalService 實(shí)例添加到 ExternalDataExchangeService 中。

以下所示的 Detach 方法通過調(diào)用 StopRuntime 來關(guān)閉運(yùn)行時(shí)。

void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
{
workflowRuntime.StopRuntime();
}

由于 WorkflowRuntime 在服務(wù)宿主啟動(dòng)過程中創(chuàng)建并初始化,因此在服務(wù)調(diào)用執(zhí)行前,任何現(xiàn)有的工作流都可以繼續(xù)工作。當(dāng)服務(wù)宿主終止時(shí),工作流運(yùn)行時(shí)會(huì)隨之徹底關(guān)閉。

注意 在托管工作流和構(gòu)建長期工作流模型時(shí),建議您使用工作流持久性服務(wù)(如 SqlWorkflowPersistenceService),這是一條基本的準(zhǔn)則。這將提供一個(gè)實(shí)現(xiàn)狀態(tài)持久性的機(jī)制,即使重新啟動(dòng)應(yīng)用程序或進(jìn)程也不會(huì)受到影響。

創(chuàng)建服務(wù)操作

要?jiǎng)?chuàng)建包含服務(wù)行為的類,必須實(shí)現(xiàn)一個(gè)或多個(gè)用于定義服務(wù)約定的接口。

public class ExpenseService :
IExpenseService,
IExpenseServiceClient,
IExpenseServiceManager

為了與工作流相集成,我們的服務(wù)方法將不包含業(yè)務(wù)邏輯,而是包含一些代碼,用以控制事件或?qū)⑹录唤o將封裝業(yè)務(wù)流程的運(yùn)行中工作流。

對(duì)于處理工作流的操作,我們將啟動(dòng)新的工作流,或者與現(xiàn)有的運(yùn)行中工作流進(jìn)行交互。

要?jiǎng)?chuàng)建新的工作流實(shí)例,需要使用 WorkflowRuntime 來實(shí)例化所需工作流類型的一個(gè)新實(shí)例。我們已在 ServiceHost 擴(kuò)展類中創(chuàng)建了一個(gè)這樣的實(shí)例。為了獲取對(duì)該實(shí)例的引用,我們必須使用 OperationContext 來找到自定義的擴(kuò)展。

WfWcfExtension extension =
OperationContext.Current.Host.Extensions.Find<WfWcfExtension>();
workflowRuntime = extension.WorkflowRuntime;

OperationContext 是供我們?cè)L問服務(wù)方法擴(kuò)展上下文的類。正如您在之前的代碼中所看到的,它提供一個(gè)名為 Current 的單例,來為我們展示當(dāng)前服務(wù)方法的上下文。我們調(diào)用 Host 屬性來將實(shí)例返回給所屬的 ServiceHost,然后根據(jù)其類型找到擴(kuò)展。

在獲得擴(kuò)展實(shí)例的引用后,可以通過公共屬性返回 WorkflowRuntime,并用它來創(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);

在上面的代碼中,我們基于預(yù)定義的類型創(chuàng)建了新的工作流實(shí)例。雖然這也可以通過直接的類型實(shí)例化來實(shí)現(xiàn),但上述的方式告訴我們,其實(shí)可以在運(yùn)行時(shí)基于動(dòng)態(tài)規(guī)則(而不是通過強(qiáng)類型化綁定)來靈活地創(chuàng)建工作流。

最后一行代碼會(huì)引發(fā)由工作流中第一個(gè) HandleExternalEventActivity 處理的事件,標(biāo)志著工作流的開始。我們通過 ExpenseLocalService 類的實(shí)例來引發(fā)這一事件。在本示例中,ExpenseLocalService 會(huì)啟動(dòng)新的工作流或?qū)⑹录唤o現(xiàn)有工作流,從而與工作流進(jìn)行交互。我們將此類用作封裝業(yè)務(wù)流程的機(jī)制。在內(nèi)部,我們使用 WF 來實(shí)現(xiàn)這一機(jī)制。

.

圖 3. 工作流始于 HandleExternalEventActivity。

我們將要應(yīng)對(duì)的另一類情況是如何回調(diào)到現(xiàn)有工作流中并引發(fā)事件。我們必須將事件交給將促使現(xiàn)有工作流接收事件并繼續(xù)進(jìn)行處理的工作流引擎。

在“開支報(bào)告”流程中引發(fā)事件的一個(gè)示例就是在需要管理人員進(jìn)行審批之時(shí)發(fā)生的。工作流將對(duì) RequestManagerApproval 調(diào)用外部方法,用以向管理人員發(fā)出警報(bào),告訴他們必須批準(zhǔn)或拒絕新的開支報(bào)告。

工作流中包含在一個(gè)可能事件發(fā)生前一直處于阻塞狀態(tài)的 ListenActivity。在本例中,我們會(huì)接收到一個(gè)事件,指示管理人員已經(jīng)審核該報(bào)告,或者已經(jīng)超過 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 審核報(bào)告時(shí),會(huì)對(duì)宿主進(jìn)行服務(wù)回調(diào),來調(diào)用引發(fā) ExpenseReportReviewed 事件的 SubmitReviewedExpenseReport 方法。

引發(fā)交給工作流中 HandleExternalEventActivity 的事件時(shí),必須知道所處理工作流實(shí)例的 GUID,使事件能夠得以路由。

每個(gè)事件都以 EventArgs 引發(fā),使我們能夠通過事件模型將數(shù)據(jù)傳遞回工作流。在本例中,我們可以傳遞詳細(xì)的當(dāng)前報(bào)表狀態(tài)信息和審核活動(dòng)上下文數(shù)據(jù)信息。

在工作流中,事件通過 HandleExternalEventActivity 的屬性自動(dòng)綁定到工作流。

.

圖 5. 將 HandleExternalEventActivity 綁定到 IExpenseLocalService 接口。

首先指定必須以 [ExternalDataExchange] 屬性標(biāo)記的接口類型,然后指定該接口上 HandleExternalEventActivity 將訂閱的事件。

事件參數(shù)必須從 ExternalDataEventArgs 類派生而來。這至少意味著每個(gè)事件都會(huì)包含上下文(例如工作流的 InstanceId)。然后,工作流運(yùn)行時(shí)負(fù)責(zé)將事件路由給正確的工作流實(shí)例,以使工作流繼續(xù)進(jìn)行。如果使用持久性服務(wù),運(yùn)行時(shí)還會(huì)在整個(gè)執(zhí)行期間管理工作流所有運(yùn)行狀態(tài)的水化和再水化。

托管服務(wù)

要托管 WCF 服務(wù),必須在 ServiceHost 容器內(nèi)運(yùn)行。

為了了解如何使用 WCF 實(shí)現(xiàn)托管,首先讓我們來了解一下幾種可用的備選方案:

?

對(duì)于標(biāo)準(zhǔn)的 Windows 進(jìn)程,可以手動(dòng)創(chuàng)建并打開 ServiceHost 實(shí)例。

?

在通過 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) 來托管端點(diǎn)。

構(gòu)建 Web 服務(wù)時(shí),您通常會(huì)選擇使用 Internet Information Services 進(jìn)行托管。而在構(gòu)建將用作后臺(tái)程序的單個(gè)實(shí)例端口時(shí),您通常會(huì)選擇通過 Windows 服務(wù)進(jìn)行托管。

在本示例中,我們?cè)谝粋€(gè) Windows 控制臺(tái)應(yīng)用程序中托管主要的服務(wù)實(shí)例,托管方式與 Windows 服務(wù)的托管方式類似。

為了部署服務(wù),我們必須創(chuàng)建 ServiceHost 類的一個(gè)實(shí)例,并針對(duì)要發(fā)布的每個(gè)服務(wù)類型開放其端點(diǎn)。ServiceHost 將許多參數(shù)作為其構(gòu)造函數(shù)的一部分;但主要參數(shù)是 Type 參數(shù)或?qū)崿F(xiàn) ServiceContract 的類的實(shí)例。

?

如果要采用 PerCall 或 PerSession 實(shí)例化,請(qǐng)使用 Type。

?

如果采用 Single 實(shí)例化,請(qǐng)使用單個(gè)實(shí)例。

有關(guān) WCF 中實(shí)例化與并發(fā)性的詳細(xì)信息,請(qǐng)參閱 MSDN 庫中的 Sessions, Instancing, and Concurrency。

宿主建立起來后,會(huì)分析所有可用的配置(在下面的配置部署部分做詳細(xì)介紹)并將其與任何明確添加的配置合并,來確定可用的端點(diǎn)并針對(duì)發(fā)布開放那些端點(diǎn)。宿主接收到從客戶端發(fā)來的調(diào)用后,會(huì)在新的后臺(tái)工作線程上處理請(qǐng)求,并按消息中 SOAP 約定名稱和動(dòng)作的指示,將其路由給相應(yī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í),必須首先為連接開放端點(diǎn)。為此,您可以在調(diào)用 .Open() 之前與宿主對(duì)象進(jìn)行交互,如前面代碼所示。建議您利用作用域在使用前對(duì) ServiceHost 進(jìn)行處置,并建議您在該作用域結(jié)束時(shí)明確地調(diào)用 Close() 來徹底關(guān)閉所有活動(dòng)的連接和端點(diǎn)。

配置部署

WCF 提供了一種機(jī)制,允許通過 XML 配置來配置端點(diǎn),從而將部署問題從實(shí)現(xiàn)工作中分離出來。這使得管理員無需重新部署代碼即可修改服務(wù)策略。

每個(gè)服務(wù)在一個(gè)或多個(gè)端點(diǎn)上發(fā)布。一個(gè)端點(diǎn)就是一個(gè)可尋址的連接點(diǎn),客戶端可對(duì)其使用服務(wù)。在 WCF 中,每個(gè)端點(diǎn)都以三個(gè)屬性聲明,這三個(gè)屬性就是人們所熟知的 WCF 的 ABC。

它們是“地址”(A)、“綁定”(B) 和“約定”(C)。

地址:此端點(diǎn)的唯一可尋址位置。通常情況下,地址是為您提供絕對(duì)地址的 URI,服務(wù)在該地址處偵聽請(qǐng)求,例如:http://myhost/myservice 或 net.tcp://myhost:400/myservice

綁定:用于規(guī)定服務(wù)與其使用者之間通信協(xié)議的策略。綁定指定了幾個(gè)方面的要素,例如所使用的傳輸類型、消息的編碼方式以及數(shù)據(jù)的序列化方式。WCF 附帶了許多用于支持最常見情況的現(xiàn)成綁定。

約定:通過接口以代碼形式定義的、要發(fā)布的操作和數(shù)據(jù)。

要配置服務(wù),我們必須對(duì)聲明服務(wù)的配置進(jìn)行聲明,并為服務(wù)配置任意數(shù)量的端點(diǎn)。由于服務(wù)可能正在實(shí)現(xiàn)任意多個(gè)約定,因此這還將影響到需要發(fā)布的端點(diǎn)數(shù)。

以下是一個(gè)配置示例。

<services>
<service name="ExpenseServices.ExpenseService">
<endpoint
address="http://localhost:8081/ExpenseService/Manager"
binding="wsHttpBinding"
contract="ExpenseContracts.IExpenseServiceManager" />
<endpoint
address="http://localhost:8081/ExpenseService/Client"
binding="wsDualHttpBinding"
contract="ExpenseContracts.IExpenseServiceClient" />
</service>
</services>

在此配置示例中,我們?yōu)?ExpenseServices.ExpenseService 類型的服務(wù)聲明配置。這樣,運(yùn)行時(shí)就可以在我們根據(jù)此類型實(shí)例化新的 ServiceHost 時(shí)找到該配置。有關(guān)綁定的詳細(xì)信息,請(qǐng)參閱 MSDN 庫中的 WCF Bindings。

使用服務(wù)

通過 ChannelFactory 類,可以利用 WCF 來使用服務(wù)。ChannelFactory 通過工廠模式為我們提供連接到配置中指定端點(diǎn)的服務(wù)約定代理實(shí)例。我們可以使用運(yùn)行時(shí)信息(例如用于消息加密的安全憑據(jù)和證書)配置工廠,或者動(dòng)態(tài)地確定端點(diǎn)信息。

private IExpenseServiceManager CreateChannelExpenseServiceManager()
{
ChannelFactory<IExpenseServiceManager> factory = new
ChannelFactory<IExpenseServiceManager>("ExpenseServiceManager");
IExpenseServiceManager proxy = factory.CreateChannel();
return proxy;
}

如您所見,我們首先創(chuàng)建了一個(gè)工廠實(shí)例,該實(shí)例對(duì)服務(wù)約定使用泛型參數(shù),以構(gòu)造出將僅返回所需約定實(shí)例的更精確的工廠。我們還指定了用于確定端點(diǎn)所用配置的參數(shù)。在本例中,我們使用名為 ExpenseServiceManager 的端點(diǎn)配置,它就是我們應(yīng)用程序配置文件中的配置。

<system.serviceModel>
<client>
<endpoint name="ExpenseServiceManager"
address="http://localhost:8081/ExpenseService/Manager"
binding="wsHttpBinding"
contract="ExpenseContracts.IExpenseServiceManager" />
</client>
</system.serviceModel>

您會(huì)發(fā)現(xiàn)端點(diǎn)定義與在宿主配置中聲明的定義完全相符。通常,只有在因網(wǎng)絡(luò)配置而造成客戶端與服務(wù)器的地址不同時(shí),或者在實(shí)現(xiàn)自定義行為時(shí),才會(huì)出現(xiàn)配置的差異。

如果安裝了 Windows SDK,您會(huì)發(fā)現(xiàn)一個(gè)用于自動(dòng)創(chuàng)建代理類和端點(diǎn)配置的工具,您可以將其集成到您的解決方案中。目標(biāo)服務(wù)必須通過 WSDL 或 WS-MetadataExchange 發(fā)布其元數(shù)據(jù)的說明,才能利用此工具。

配置雙工信道

到現(xiàn)在為止,我們一直在假設(shè)我們的通信流采用的是請(qǐng)求/響應(yīng)協(xié)作模式,消息由使用者發(fā)出并由服務(wù)做出應(yīng)答。WCF 支持許多備選的消息流,例如單向(“即發(fā)即棄”模式)或雙向雙工通信。如果處理每一方都可以啟動(dòng)會(huì)話的消息流,則需要使用雙工或雙向信道。對(duì)于那些連接更為緊密且支持沿任一方向發(fā)送數(shù)據(jù)的系統(tǒng)而言,雙工信道非常有效。例如,如果要提供始于事件處理過程的回調(diào),雙工信道將非常有用。

實(shí)現(xiàn)客戶端回調(diào)

在 WCF 中,客戶端回調(diào)通過一個(gè)稱為 CallbackContracts 的概念來實(shí)現(xiàn)。對(duì)于所發(fā)布的約定,我們可以指定另一個(gè)約定來定義客戶端將發(fā)布的、可由服務(wù)上運(yùn)行的代碼回調(diào)的操作。

要聲明 CallbackContract,應(yīng)在發(fā)出回調(diào)的服務(wù)約定中指定接口類型。

[ServiceContract(CallbackContract =
typeof(IExpenseServiceClientCallback))]

您還需要使用支持雙工信道的綁定,如 netTcpBinding 或 wsDualHttpBinding?;?TCP 的雙工通信通過在整個(gè)消息交換過程中所建立和維護(hù)的雙向連接來實(shí)現(xiàn)。而基于 HTTP 的雙工通信則通過對(duì)客戶端偵聽程序的回調(diào)來實(shí)現(xiàn)。由于客戶端可能不知道其返回路徑,或者您可能希望通過配置對(duì)其進(jìn)行強(qiáng)定義,因此我們可以使用自定義綁定配置來聲明一個(gè)替代的 clientBaseAddress。

<endpoint binding="wsDualHttpBinding"
bindingConfiguration="AlternativeClientCallback"/>
<bindings>
<wsDualHttpBinding>
<binding name="AlternativeClientCallback"
clientBaseAddress="http://localhost:8082/ExpenseService/ClientCallback"/>
</wsDualHttpBinding>
</bindings>

在客戶端實(shí)現(xiàn)回調(diào)

實(shí)現(xiàn)回調(diào)約定的方式與實(shí)現(xiàn)服務(wù)約定的方式是完全相同的。我們必須提供所定義接口的實(shí)現(xiàn)。

class CallbackHandler : IExpenseServiceClientCallback
{
public void ExpenseReportReviewed(
ExpenseReportReviewedRequest expenseReportReviewedRequest)
{
// 在此實(shí)現(xiàn)客戶端邏輯以對(duì)回調(diào)作出響應(yīng)。
}
}

為了使宿主回調(diào)到 CallbackHandler 類的實(shí)例,建立的客戶端信道必須能夠知曉連接的雙工特性。

如之前所述,首先使用支持雙工信道的綁定。其次,在初始化與服務(wù)端點(diǎn)的連接時(shí),使用名為 DuplexChannelFactory 的 ChannelFactory 子類版本,它將創(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 類的實(shí)例,并將其傳遞給工廠構(gòu)造函數(shù),以便初始化回調(diào)所使用的上下文。

實(shí)現(xiàn)宿主的回調(diào)

從宿主的角度來看,我們可以通過在 IExpenseServiceClient 約定中定義的回調(diào)信道來獲取對(duì)面向客戶端的回調(diào)的一個(gè)引用。

[ServiceContract(CallbackContract =
typeof(IExpenseServiceClientCallback))]
public interface IExpenseServiceClient : IExpenseService

CallbackContract 屬性聲明用于定義宿主回調(diào)約定的接口。

為了進(jìn)行回調(diào),我們通過調(diào)用 OperationContext.Current.GetCallbackChannel 來獲取回調(diào)約定的引用,如下所示。

IExpenseServiceClientCallback callback =
OperationContext.Current.GetCallbackChannel
<IExpenseServiceClientCallback>();
callback.ExpenseReportReviewed(new
ExpenseReportReviewedRequest(e.Report));

在獲得對(duì)回調(diào)信道的引用后,我們即可正常地進(jìn)行調(diào)用。

結(jié)束語

Windows Workflow Foundation 提供了一個(gè)用于定義工作流的通用框架,以及一個(gè)用于托管運(yùn)行中工作流并與之進(jìn)行交互的、穩(wěn)健的運(yùn)行時(shí)引擎。

Windows Communication Foundation 提供了用于構(gòu)建互聯(lián)系統(tǒng)的通用框架,并為開發(fā)人員提供了一致的 API 和豐富的功能集,來定義通信方式。

您可以將這兩種框架結(jié)合起來,為在所處環(huán)境中構(gòu)建和部署分布式業(yè)務(wù)流程提供靈活而全面的應(yīng)用程序平臺(tái)。WF 允許您為業(yè)務(wù)邏輯和流程建模并進(jìn)行封裝,而 WCF 又為您提供了具有多種系統(tǒng)分布方式選擇的消息傳遞基礎(chǔ)結(jié)構(gòu)。

以下是您在設(shè)計(jì)服務(wù)時(shí)需要牢記的幾條原則:

?

應(yīng)通過使用持久性服務(wù)來支持長時(shí)間運(yùn)行的工作流。

?

服務(wù)操作可以通過運(yùn)行工作流、通過引發(fā)事件進(jìn)行交互。應(yīng)將工作流設(shè)計(jì)為在需要介入時(shí)引發(fā)事件,在與外部(例如,外部服務(wù)或人員)進(jìn)行交互時(shí)響應(yīng)事件。

?

工作流將異步執(zhí)行服務(wù)調(diào)用,因此考慮自服務(wù)返回的數(shù)據(jù)及當(dāng)時(shí)數(shù)據(jù)所處的狀態(tài),可有針對(duì)性地進(jìn)行設(shè)計(jì)。如果要使用同步方法,可以利用 ManualWorkflowSchedulerService 類對(duì)工作流的執(zhí)行進(jìn)行手動(dòng)調(diào)度。

更多信息

?

Sessions, Instancing, and Concurrency

?

WCF Bindings

?

Using Data Contracts

?

Using Message Contracts

?

.NET Framework 3.0 社區(qū)站點(diǎn)

?

Windows Workflow Foundation 論壇

致謝

特此對(duì)以下付出過辛勤勞動(dòng)的貢獻(xiàn)者和審核人員表示萬分的感謝:

?

Christian Weyer, thinktecture

?

Paul Andrew, Microsoft Corporation

?

Khalid Aggag, Microsoft Corporation

?

Patrice Manac‘h, Microsoft Corporation

作者簡介

Jeremy Boyd 是 Intergen 公司(總部位于新西蘭的解決方案供應(yīng)商,是 Microsoft 金牌認(rèn)證合作伙伴)的高級(jí)技術(shù)顧問,也是 MSDN 新西蘭區(qū)區(qū)域總監(jiān)。在過去的 12 個(gè)月里,Jeremy 與客戶積極合作,致力于幫助實(shí)現(xiàn)基于 WF 和 WCF 的解決方案,并通過他的博客幫助其他開發(fā)人員了解這些技術(shù)所帶來的益處。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多