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