一、簡介
使用InvokeWorkflowActivity 活動可以從一個工作流中異步方式啟動另一個工作流。 在已啟動的工作流開始執行且工作流分支中的下一個活動執行之前,InvokeWorkflowActivity 活動即告完成。
注意:
* WF不支持遞歸工作流。如果工作流A能夠啟動工作流B,則工作流B既不能直接啟動工作流A,也不能啟動任何直接或間接調用工作流A的工作流。
* InvokeWorkflowActivity活動要求工作流運行時使用當前附加到該運行時的計劃程序服務創建新工作流。
* 所調用的工作流將只能夠接收輸入參數。不支持在工作流完成之後獲取輸出參數,因為該活動以異步方式調用工作流。
* 工作流之間的標准通信規則適用於與InvokeWorkflowActivny活動所創建的新工作流實例進行通信。
使用InvokeWorkflowActivity活動的步驟如下:
* 拖動一個InvokeWorkflowActivity到工作流中希望的位置處。
* 設置TargetWorkflow屬性為希望執行的工作流的類型(Type)。
* 為TargetWorkflow設置所需要的值。
當設置TargetWorkflow屬性時,該活動提供了對話框允許從所有引用到的活動類型列表中導航到正確的類型,但是只有派生自Activity的類會被顯示在列表中。為了引用一個新的工作流類型,必須首先添加到包含工作流的項目或程序集的引用。具體對話框請參考本文後面的圖示。
一旦定義了TargetWorkflow屬性,工作流的參數集合屬性將使用定義在TargetWorkflow中的其他任何屬性所更新。允許開發人員在屬性窗口中為任何所需的屬性設置值,可以設置靜態值或者是綁定屬性到當前工作流的其他屬性或者是其他活動的其他屬性。
InvokeWorkflowActivity提供了一個Invoking事件允許開發人員使用代碼處理。該事件在創建一個新的工作流之前觸發,這使開發人員在開始一個新的工作流之前能夠有機會完成一些設置任務。
關於InvokeWorkflowActiv時的一個重要方面工作流將以異步的方式執行,因此不會等待新工作流的執行完成。因為執行過程是異步的,所以無法獲取另一個工作流的輸出參數。通常需要和宿主建立額外的通信機制來獲取其輸出。
二、創建控制台順序工作流示例程序框架
說明:本文創建的InvokeWorkflowActivityDemo示例演示了如何在一個狀態機工作流內部調用另外的一個工作 SubWorkflow,並且定義了本地服務接口實現,使用HandleExternalEvent活動調用外部事件以等待被調用的工作流實例執行完成。該活動需要等待一個事件的觸發才能夠繼續工作流的運行,而在Program.cs中,設置了只有當指定非宿主工作流執行完畢後,才觸發事件。因此這實現了一種等待被調用工作流執行完成才繼續執行的效果。
重要提示:
本實例的學習基於WWF中的許多新概念(不包括在以前的教程中),請結合後面的參考資料全面理解。個別難點,請不必過於擔心,我會在後面的系列文章中作細致的剖析。
請遵循如下步驟創建一個控制台狀態機工作流示例程序:
1.啟動VS2008,單擊菜單”文件“|”新建“|”項目“,創建一個名字為InvokeWorkflowActivityDemo的控制台狀態機工作流示例程序。
2.之後,系統自動打開工作流設計器界面。
3.從工具箱中拖動四個State活動到工作流設計器中。然後,再依次把兩個StateInitialization活動分別拖動到前兩個State活動中,再拖動一個EventDrivenActivity活動到第三個State活動中。最後,再使用拖動手柄的方法創建四個State活動的轉換關系,得到如圖所示的情形。
三、添加另一個子狀態機工作流
右單擊示例工程,選擇“添加”-“State Machine Workflow...”(如下圖所示),添加一個狀態機工作流,命名為SubWF(見後面的圖)。
在上圖子狀態機設計器中僅拖入一個StateInitialization活動和一個StateFinalization活動,如下圖所示。
右單擊第二個活動,選擇“設置為已完成狀態”選項,使之成為整個子工作流的最終狀態。然後,雙擊第一個狀態中的StateInitializationActivity1活動,創建如下圖所示的簡單活動流程。
雙擊子活動codeActivity1,輸入以下代碼:
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("這是發自子狀態機中的消息。");
}
四、使用InvokeWorkflowActivity 活動調用子狀態機工作流
在宿主工作流的stateActivity1狀態活動中,添加了一個 InvokeWorkflowActivity(雙擊stateActivity1狀態活動內部的 stateInitializationActivity1,然後拖入一個InvokeWorkflowActivity),如下圖所示。
將InvokeWorkflowActivity1的TargetWorkflow指定為當前項目中前面創建的SubWF工作流,相關設置對話框如下圖所示。
InvokeWorkflowActivity是放置在StatelnitializationActiv時容器內部的,以便於在進入StateActivity活動時總是最先運行子工作流。
五、添加HandleExternalEventActivity活動
在宿主工作流的stateActivity2內部的EventDrivenActivitv內部放置拖入一個HandleExternalEventActivity。
注意:HandleExternalEventActivity活動用於等待本地服務中的InvokedWorkflowComplete事件觸發,並阻止當前工作流的繼續執行。
對於HandleExternalEventActivity活動, 必須設置它的參數InterfaceType和EventName。方法是,單擊屬性窗口中參數InterfaceType右邊的“...”符號,彈出一個對話框如下所示:
從右圖選定我們事先已定義好的接口,單擊“確定”按鈕。
然後,單擊屬性窗口中參數EventName右邊的下拉箭頭,從中選擇已經在選定的接口中聲明的事件(在本例中是InvokedWorkflowComplete)。
六、定義在工作流實例與宿主間通信的本地服務
WWF中的服務可分為核心服務和本地服務。核心服務由WF定義,而本地服務(也稱為數據交換服務) 則是開發人員自定義的。本地服務可以是任何想在WF中實現的服務,一個通常的用處是使用本地服務在工作流實例與宿主之間進行通信。有關於“本地服務”的全面討論是一個復雜的話題,我想在本系列後面的學習教程中對之展開全面深入的探討,在此不贅述。
[一]定義接口
根據WWF中本地服務的定義要求,首先要定義一個修飾以ExternalDataExchange屬性的接口,我們對之命名為ILocalService,代碼如下:
[ExternalDataExchange]
internal interface ILocalService
{
event EventHandler<ExternalDataEventArgs> InvokedWorkflowComplete;
void WorkComplete(Guid HostWFGuid);
}
[二]創建本地服務類
然後,基於上述接口創建一個本地服務類,代碼如下:
//定義一個本地服務實現,該服務將被添加到工作流運行時引擎中
internal class LocalService : ILocalService
{
public event EventHandler<ExternalDataEventArgs> InvokedWorkflowComplete;
public void WorkComplete(Guid HostWFGuid)//實現接口中聲明的方法
{
if (InvokedWorkflowComplete != null)
{
InvokedWorkflowComplete(null, new ExternalDataEventArgs(HostWFGuid));
}
}
}
上面定義中,基本遵循了“死”格式,除了方法名稱外。在本文中先不詳細討論。
七、在宿主中操作工作流實例與本地服務
根據前面的要求,本地服務負責工作流實例與宿主間的通信中介,而加載本地服務的工作是在宿主中完成的。下面給出了宿主部分(program.cs)完整的代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;//ExternalDataExehangeService
namespace InvokeWorkflowActivityDemo
{
class Program
{
static Guid HostWFGuid;//用於記憶父工作流實例的ID標記
static LocalService ls;
static void Main(string[] args)
{
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
//下面這幾行是必需的死套路
//加載本地服務
ExternalDataExchangeService dataService = new ExternalDataExchangeService();
workflowRuntime.AddService(dataService);
//將自定義的本地通信服務加載到本地服務中
ls = new LocalService();
dataService.AddService(ls);
//事件初始狀態為終止狀態(此時任何線程可以使用此事件)
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{
//如果將要產生僅一個子工作流,那麼需要檢查工作流是否完成,而不是主工作流。
if (e.WorkflowInstance.InstanceId != HostWFGuid)
{
//通過本地服務的特定方法通知主工作流,調用工作流完成
ls.WorkComplete(HostWFGuid);
}
else
{
//此時是主工作流自身,將事件的狀態位置設置為終止狀態,允許一個或多個等待線程繼續。
waitHandle.Set();
}
};
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
//啟動父工作流實例
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1));
HostWFGuid = instance.InstanceId;//記下父工作流實例的ID標記
instance.Start();
waitHandle.WaitOne();//阻止當前線程,直到當前waitHandle收到信號。
Console.ReadLine();
}
}
}
}
根據MSDN聲明,啟動工作流的實例之前,必須將 ExternalDataExchangeService 添加到工作流運行時引擎,然後將自定義通信服務添加到 ExternalDataExchangeService。詳情請參考MSDN文章《在工作流中使用本地服務》。
八、運行實例
按F5運行控制台程序,一般順利的話,將得到如下圖所示運行時快照。
出處: http://zhuxianzhong.blog.51cto.com/157061/203672