Dino Esposito
Solid Quality Learning
適用于:
Microsoft Windows Workflow Foundation
Microsoft Windows Vista
摘要:對(duì)于需要為 Microsoft .NET 平臺(tái)創(chuàng)建工作流驅(qū)動(dòng)應(yīng)用程序的開(kāi)發(fā)人員而言,本文將介紹他們感興趣的 Microsoft Windows Workflow Foundation 技術(shù)和功能。
注本文撰寫的對(duì)象為 Windows Workflow Foundation beta 1。請(qǐng)注意,在該技術(shù)的最終版本問(wèn)世之前,內(nèi)容上很可能會(huì)發(fā)生更改。
本頁(yè)內(nèi)容
有關(guān)向 Windows 平臺(tái)添加工作流支持的初步知識(shí)
創(chuàng)建第一個(gè)工作流
接收和使用數(shù)據(jù)
工作流運(yùn)行庫(kù)
工作流和活動(dòng)
開(kāi)發(fā)自定義活動(dòng)
計(jì)劃更現(xiàn)實(shí)的工作流
小結(jié)
有關(guān)向 Windows 平臺(tái)添加工作流支持的初步知識(shí)
Microsoft Windows Workflow Foundation (WWF) 是一個(gè)可擴(kuò)展框架,用于在 Windows 平臺(tái)上開(kāi)發(fā)工作流解決方案。作為即將問(wèn)世的 Microsoft WinFX 的組成部分,Windows Workflow Foundation 同時(shí)提供了 API 和一些工具,用于開(kāi)發(fā)和執(zhí)行基于工作流的應(yīng)用程序。Windows Workflow Foundation 提供單個(gè)統(tǒng)一的模型,以便創(chuàng)建跨越多個(gè)類別應(yīng)用程序的端到端解決方案,包括人力工作流和系統(tǒng)工作流。
Windows Workflow Foundation 是一個(gè)廣泛且通用的工作流框架,并且從下到上、在每個(gè)級(jí)別都針對(duì)可擴(kuò)展性進(jìn)行了設(shè)計(jì)。基于 Windows Workflow Foundation 的解決方案,由得到 Microsoft .NET 代碼支持且在宿主應(yīng)用程序中運(yùn)行的互連組件組成。就像在定制的環(huán)境中以可視方式創(chuàng)建 Web 頁(yè)一樣,您需要在可視設(shè)計(jì)器中制訂特定工作流的步驟,并且添加代碼隱藏工作流組件以實(shí)現(xiàn)規(guī)則并定義業(yè)務(wù)過(guò)程。
Windows Workflow Foundation 提供一個(gè)工作流引擎、一個(gè) .NET 托管 API、運(yùn)行庫(kù)服務(wù)以及與 Microsoft Visual Studio 2005 集成的可視化設(shè)計(jì)器和調(diào)試器。可使用 Windows Workflow Foundation 來(lái)生成并執(zhí)行同時(shí)跨越客戶端和服務(wù)器的工作流,以及可在所有類型的 .NET 應(yīng)用程序內(nèi)部執(zhí)行的工作流。
本文通過(guò)幾個(gè)循序漸進(jìn)的示例對(duì) Windows Workflow Foundation 的進(jìn)行了流暢的簡(jiǎn)介,并且說(shuō)明它的工作方式。
工作流 是以活動(dòng)示意圖形式定義的人力或系統(tǒng)過(guò)程模型。活動(dòng) 是工作流中的一個(gè)步驟,并且是工作流的執(zhí)行、重用和創(chuàng)作單位?;顒?dòng)示意圖表達(dá)規(guī)則、操作、狀態(tài)以及它們的關(guān)系。Windows Workflow Foundation 工作流通過(guò)安排活動(dòng)而設(shè)計(jì),然后它編譯為 .NET 程序集,且在工作流運(yùn)行庫(kù)和公共語(yǔ)言運(yùn)行庫(kù) (CLR) 中執(zhí)行。
創(chuàng)建第一個(gè)工作流
Windows Workflow Foundation 主要由 .NET 驅(qū)動(dòng)的運(yùn)行庫(kù)環(huán)境組成,該環(huán)境處理在 Visual Studio 設(shè)計(jì)器中設(shè)計(jì)和實(shí)現(xiàn)的特殊對(duì)象。Microsoft .NET Framework 2.0 是支持 Windows Workflow Foundation 所必需的。單獨(dú)的安裝程序包為 Visual Studio 2005 添加了 Windows Workflow Foundation 設(shè)計(jì)器和項(xiàng)目模板支持。一旦安裝,就會(huì)向 Visual Studio 2005 中的標(biāo)準(zhǔn)項(xiàng)目列表中添加一個(gè)全新的節(jié)點(diǎn),如圖 1 所示。

您可以在各種選項(xiàng)中進(jìn)行選擇,其中每個(gè)選項(xiàng)都標(biāo)識(shí)了特定類型的工作流應(yīng)用程序。表 1 顯示工作流項(xiàng)目模板的不完全列表。
|
類型 |
說(shuō)明 |
|---|---|
|
順序工作流控制臺(tái)應(yīng)用程序 (Sequential Workflow Console Application) |
創(chuàng)建用于生成工作流的項(xiàng)目,該工作流包含一個(gè)默認(rèn)的順序工作流和一個(gè)控制臺(tái)測(cè)試宿主應(yīng)用程序。 |
|
順序工作流庫(kù) (Sequential Workflow Library) |
創(chuàng)建用于以庫(kù)的形式生成順序工作流的項(xiàng)目。 |
|
工作流活動(dòng)庫(kù) (Workflow Activity Library) |
創(chuàng)建一個(gè)用來(lái)創(chuàng)建活動(dòng)的庫(kù)的項(xiàng)目,以后可以將其作為工作流應(yīng)用程序中的構(gòu)造塊重用。 |
|
狀態(tài)機(jī)控制臺(tái)應(yīng)用程序 (State Machine Console Application) |
創(chuàng)建用于生成狀態(tài)機(jī)工作流和控制臺(tái)宿主應(yīng)用程序的項(xiàng)目。 |
|
狀態(tài)機(jī)工作流庫(kù) (State Machine Workflow Library) |
創(chuàng)建用于以庫(kù)的形式生成狀態(tài)機(jī)工作流的項(xiàng)目。 |
|
空工作流 (Empty Workflow) |
創(chuàng)建可以包含工作流和活動(dòng)的空項(xiàng)目。 |
Windows Workflow Foundation 支持兩種基本工作流樣式:順序工作流和狀態(tài)機(jī)工作流。
順序工作流 非常適合以下類型的操作,即該操作由依次執(zhí)行直至最后一個(gè)活動(dòng)完成的步驟的管線表示。但是,順序工作流的執(zhí)行并非完全是順序的。它們?nèi)匀豢梢越邮胀獠渴录蛘邌?dòng)并行任務(wù),在這種情況下,確切的執(zhí)行順序可能有所不同。
狀態(tài)機(jī)工作流 由一組狀態(tài)、轉(zhuǎn)換和操作組成。首先,將一個(gè)狀態(tài)表示為起始狀態(tài),然后,基于事件執(zhí)行向另一個(gè)狀態(tài)的轉(zhuǎn)換。狀態(tài)機(jī)工作流可以具有確定工作流結(jié)束的最終狀態(tài)。
讓我們假設(shè)您選擇并創(chuàng)建了一個(gè)新的順序工作流控制臺(tái)應(yīng)用程序項(xiàng)目。Visual Studio 2005 解決方案資源管理器將包含兩個(gè)文件 — workflow1.cs 以及最初從視圖中隱藏的 workflow1.designer.cs。這兩個(gè)文件表示創(chuàng)建的工作流。Windows Workflow Foundation 工作流包含工作流模型文件和一個(gè)代碼文件類。workflow1.cs 類是可在其中寫入您自己的工作流業(yè)務(wù)邏輯的代碼文件類。workflow1.designer.cs 類表示活動(dòng)示意圖的說(shuō)明。該文件由 Visual Studio 2005 按照與 Microsoft Windows Forms 項(xiàng)目中的窗體非常類似的方式自動(dòng)管理。當(dāng)向工作流中添加活動(dòng)時(shí),Visual Studio 2005 會(huì)用以編程方式生成活動(dòng)示意圖的 Microsoft C# 代碼更新設(shè)計(jì)器類。要繼續(xù)使用 Windows 窗體的類比,那么工作流類似于窗體,而活動(dòng)類似于控件。
我們可以為活動(dòng)布局選擇另一種形式的持久性 — XML 工作流標(biāo)記格式。要嘗試這一方法,可從項(xiàng)目中刪除 workflow1.cs 文件并添加一個(gè)新的工作流項(xiàng),如圖 2 所示。

現(xiàn)在,項(xiàng)目包含兩個(gè)文件:workflow1.xoml 和 workflow1.xoml.cs。前一個(gè)文件包含表示工作流模型的 XML 工作流標(biāo)記;后一個(gè)文件是一個(gè)代碼文件類,并且包含工作流的源代碼和事件處理程序。如果雙擊 .xoml 文件,會(huì)看到處于運(yùn)行狀態(tài)的可視工作流設(shè)計(jì)器(參見(jiàn)圖 3)。
不存在為工作流模型的序列化選擇標(biāo)記或代碼的運(yùn)行時(shí)暗示 — 一旦該工作流編譯為程序集,它們就是等效的。
工作流應(yīng)用程序是完成工作(例如,發(fā)送或接收數(shù)據(jù))的活動(dòng)和管理一組子活動(dòng)的執(zhí)行的復(fù)合活動(dòng)(例如,IfElse 和 While)的混合體。工作流可實(shí)現(xiàn)復(fù)雜的端到端方案,例如文檔審閱、PO 批準(zhǔn)、IT 用戶管理、合作伙伴之間的信息交換、任何種類的向?qū)Щ驑I(yè)務(wù)線應(yīng)用程序。
圖 3 顯示一個(gè)極為簡(jiǎn)單的示例工作流,它只包含一個(gè)活動(dòng) — code1 塊。

Code 塊對(duì)應(yīng)于 Code 類的一個(gè)實(shí)例,并且表示工作流中的一個(gè)活動(dòng),其行為以用戶定義的代碼表示。后端代碼是通過(guò) Visual Studio 2005 輸入的(只需雙擊設(shè)計(jì)器中的所選元素),這是 ASP.NET 應(yīng)用程序和其他 Visual Studio 2005 項(xiàng)目為人熟悉的編程風(fēng)格。
當(dāng)雙擊該活動(dòng)時(shí),代碼文件會(huì)打開(kāi)且提供代碼處理程序的存根。
private void code1_ExecuteCode(object sender, EventArgs e)
{
// Some code here
}
當(dāng)工作流運(yùn)行庫(kù)在處理工作流的過(guò)程中開(kāi)始處理指定的活動(dòng)塊時(shí),在代碼處理程序中鍵入的任何語(yǔ)句都將執(zhí)行。以下代碼只是輸出一條歡迎消息。
private void code1_ExecuteCode(object sender, EventArgs e)
{
Code c = (Code) sender;
Console.WriteLine("Hello, from '{0}'.\nI'm an instance of the {1} class.",
c.ID, c.ToString());
}
除了可視化布局以外,工作流還包含 workflow1.xoml.cs 文件中保存的以下代碼。
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace HelloWorldWorkflow
{
public partial class Workflow1 : SequentialWorkflow
{
private void code1_ExecuteCode(object sender, EventArgs e)
{
Code c = (Code) sender;
Console.WriteLine("Hello, from '{0}'.\nI'm an instance of the {1} class.",
c.ID, c.ToString());
}
}
}
partial 屬性涉及不完全類 — 這是 .NET Framework 2.0 中的一個(gè)新概念。不完全類 是這樣的一種類:它的定義可能覆蓋不同的源文件。每個(gè)源文件都包含一個(gè)有頭有尾的普通類定義,只不過(guò)該定義是不完整的,并且沒(méi)有窮盡該類所需的邏輯。編譯器將把不完全的類定義合并到該類可以編譯的完整定義中。不完全類與面向?qū)ο鬀](méi)有任何關(guān)系;它們是用來(lái)擴(kuò)展項(xiàng)目中類行為的源代碼級(jí)別和受到程序集限制的方式。在 .NET Framework 2.0 中,不完全類是用于防止 Visual Studio 2005 在代碼文件內(nèi)部注入自動(dòng)生成代碼的手段。原始類中缺少的任何綁定代碼都將由運(yùn)行庫(kù)通過(guò)添加不完全類進(jìn)行添加。
工作流只能由 Windows Workflow Foundation 工作流運(yùn)行庫(kù)執(zhí)行,并且工作流運(yùn)行庫(kù)需要外部應(yīng)用程序按照若干個(gè)規(guī)則來(lái)承載它。出于測(cè)試目的,Visual Studio 2005 還向項(xiàng)目中添加了一個(gè) program.cs 文件。該文件是一個(gè)簡(jiǎn)單的控制臺(tái)應(yīng)用程序,如下所示。
class Program
{
static AutoResetEvent waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
WorkflowRuntime workflowRuntime = new WorkflowRuntime();
workflowRuntime.StartRuntime();
workflowRuntime.WorkflowCompleted += OnWorkflowCompleted;
Type type = typeof(HelloWorldWorkflow.Workflow1);
workflowRuntime.StartWorkflow(type);
waitHandle.WaitOne();
workflowRuntime.StopRuntime();
// A bit of feedback to the user
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("==========================");
Console.WriteLine("Press any key to exit.");
Console.WriteLine("==========================");
Console.ReadLine();
}
static void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
waitHandle.Set();
}
}
正如您在上述代碼中的粗體行中看到的那樣,出于簡(jiǎn)單性的目的,Visual Studio 2005 在該控制臺(tái)應(yīng)用程序中硬編碼工作流類的名稱。要防止控制臺(tái)應(yīng)用程序在完成之后立即退出,您可能需要在 Main 方法的結(jié)尾添加 Console.ReadLine 調(diào)用。此刻,您已經(jīng)做好生成和測(cè)試該工作流的所有準(zhǔn)備:請(qǐng)按 F5 并執(zhí)行。如果一切順利,那么會(huì)看到圖 4 中顯示的輸出。

調(diào)試工作流應(yīng)用程序也很容易。實(shí)際上,需要做的所有工作就是放置斷點(diǎn)。您可以在工作流代碼文件類中的任何位置放置斷點(diǎn)(就像通常對(duì) C# 代碼所做的那樣),或者直接在設(shè)計(jì)器視圖中放置斷點(diǎn)(這確實(shí)非常有趣)。需要選擇希望調(diào)試器介入的活動(dòng),按 F9 設(shè)置斷點(diǎn),如圖 5 所示。

一旦代碼流到達(dá)設(shè)置了斷點(diǎn)的活動(dòng),Visual Studio 2005 就會(huì)將控制權(quán)移交給工作流調(diào)試器(參見(jiàn)圖 6)。從這里開(kāi)始,您可以按照預(yù)期的那樣,在可視化設(shè)計(jì)器中按 F11 單步執(zhí)行代碼和活動(dòng)。

接收和使用數(shù)據(jù)
讓我們繼續(xù)分析并修改該工作流,以使其在實(shí)例化以后接收和使用數(shù)據(jù)。有兩種在實(shí)例化工作流以后使其接收數(shù)據(jù)的常規(guī)方法:參數(shù)和事件。如果選擇使用參數(shù),則需要在可視化設(shè)計(jì)器中手動(dòng)定義參數(shù)名稱和類型的列表。如果選擇使用事件,則需要?jiǎng)?chuàng)建并添加一個(gè)自定義活動(dòng)(該活動(dòng)充當(dāng)在工作流模型中的某個(gè)位置介入的外部源),并且傳入一些數(shù)據(jù)。我們將在下文中說(shuō)明基于事件的方法;現(xiàn)在,讓我們重點(diǎn)說(shuō)明一下參數(shù)。
如圖 7 所示,Workflow1 的 Properties 窗格顯示一個(gè) Parameters 集合,您將在設(shè)計(jì)時(shí)用名稱/值對(duì)進(jìn)行填充。

圖 8 顯示正在工作的參數(shù)編輯器。您可以為所需的每個(gè)參數(shù)創(chuàng)建一個(gè)新項(xiàng)并指明它的名稱、類型和方向。

參數(shù)的類型可手動(dòng)鍵入,也可從定制的對(duì)象瀏覽器中選擇。在關(guān)閉 Workflow Parameters Editor 對(duì)話框后,立即修改代碼文件以合并剛剛定義的參數(shù)。您通常添加兩個(gè)公共屬性,并且使它們公開(kāi) Parameters 集合的內(nèi)容,如下面的代碼所示。
public partial class Workflow1 : SequentialWorkflow
{
public string UserFirstName
{
get { return (string) Parameters["FirstName"].Value; }
set { Parameters["FirstName"].Value = value; }
}
public string UserLastName
{
get { return (string) Parameters["LastName"].Value; }
set { Parameters["LastName"].Value = value; }
}
:
}
使用公共屬性確實(shí)是一種良好的編程做法,可使代碼變得更加簡(jiǎn)潔明了。它絕不是使用參數(shù)數(shù)據(jù)的要求。直接對(duì)工作流的 Parameters 集合進(jìn)行調(diào)用也是完全可行的。如果用公共屬性包裝參數(shù),則請(qǐng)隨意選擇屬性的名稱。然而,請(qǐng)記住,C# 中的參數(shù)名是區(qū)分大小寫的。
那么是誰(shuí)實(shí)際上通過(guò)這些參數(shù)提供輸入數(shù)據(jù)呢?完成這一任務(wù)的是宿主應(yīng)用程序。然而,需要注意的是,宿主在初始化時(shí)(此時(shí),工作流被加載以便執(zhí)行到運(yùn)行庫(kù)容器中)設(shè)置任何參數(shù)。為了稍微詳細(xì)地說(shuō)明這一點(diǎn),讓我們編寫一個(gè)基于 Windows 窗體的示例宿主應(yīng)用程序。該示例應(yīng)用程序?qū)⑻峁讉€(gè)文本框,以便用戶輸入名字和姓氏(參見(jiàn)圖 9),并且將它們傳遞給工作流代碼處理程序。為了使用這些參數(shù),讓我們重新編寫代碼處理程序,如下所示。
private void code1_ExecuteCode(object sender, EventArgs e)
{
MessageBox.Show("Welcome, " + UserFirstName + " " + UserLastName);
}
該示例 Windows 窗體宿主應(yīng)用程序中的關(guān)鍵內(nèi)容是附加到 Start Workflow 按鈕的單擊處理程序。

窗體的代碼隱藏類的全部源代碼如下所示。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
namespace WinFormHost
{
public partial class Form1 : Form
{
private WorkflowRuntime _wr = null;
private string _workflowAssembly = "";
private string _workflowTypeName = "";
public Form1()
{
InitializeComponent();
_workflowAssembly = "WorkflowWithParams";
_workflowTypeName = "WorkflowWithParams.Workflow1";
_wr = new WorkflowRuntime();
_wr.StartRuntime();
}
private void btnStartWorkflow_Click(object sender, EventArgs e)
{
string assemblyName = _workflowAssembly;
string typeName = _workflowTypeName;
// Attempt to get type by fully-qualified name
Assembly assembly = Assembly.Load(assemblyName);
Type workflowType = assembly.GetType(typeName);
Dictionary parameters = new Dictionary();
parameters.Add("FirstName", txtFirstName.Text);
parameters.Add("LastName", txtLastName.Text);
// Start the workflow
Guid instanceID = Guid.NewGuid();
_wr.StartWorkflow(workflowType, instanceID, parameters);
}
}
}
要填充參數(shù)集合,需要使用由字符串和對(duì)象組成的基于泛型的字典。項(xiàng)的名稱是字符串,而所包含的值被配置為對(duì)象。需要向該字典中添加像工作流模型中的靜態(tài)參數(shù)一樣多的項(xiàng) — 在此情況下,請(qǐng)?zhí)砑?FirstName 和 LastName。這兩個(gè)參數(shù)獲得在 UI 文本框中鍵入的內(nèi)容。
最后,在創(chuàng)建指定模型的實(shí)例的同時(shí)將執(zhí)行工作流。運(yùn)行庫(kù)對(duì)象上的 StartWorkflow 方法具有多個(gè)重載。代碼中使用的版本接受工作流類型、輸入?yún)?shù)的集合以及系統(tǒng)生成的全局唯一標(biāo)識(shí)符 (GUID)。
對(duì)于每個(gè)進(jìn)程,只需要一個(gè)工作流運(yùn)行庫(kù)實(shí)例;對(duì)于每個(gè) AppDomain,不能有一個(gè)以上的實(shí)例。這里,最需要做的事就是直接在窗體的構(gòu)造函數(shù)中創(chuàng)建所需的實(shí)例。同一個(gè)運(yùn)行庫(kù)對(duì)象可以照看多種工作流實(shí)例。運(yùn)行庫(kù)基于實(shí)例的 GUID 來(lái)區(qū)分它們,并且為每個(gè)特定實(shí)例接收私有數(shù)據(jù)。

出于純粹的教學(xué)目的,讓我們?cè)谶@一開(kāi)發(fā)階段迅速觀察一下設(shè)計(jì)器和工作流標(biāo)記代碼。下面是 workflow1.designer.cs 源代碼文件。
public sealed partial class Workflow1 : SequentialWorkflow
{
private void InitializeComponent()
{
ParameterDeclaration FirstName = new ParameterDeclaration();
ParameterDeclaration LastName = new ParameterDeclaration();
this.code1 = new System.Workflow.Activities.Code();
//
// code1
//
this.code1.ID = "code1";
this.code1.ExecuteCode += new System.EventHandler(this.code1_ExecuteCode);
//
// Workflow1
//
this.Activities.Add(this.code1);
this.DynamicUpdateCondition = null;
this.ID = "Workflow1";
FirstName.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
FirstName.Name = "FirstName";
FirstName.Type = typeof(string);
FirstName.Value = null;
LastName.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
LastName.Name = "LastName";
LastName.Type = typeof(string);
LastName.Value = null;
this.Parameters.Add(FirstName);
this.Parameters.Add(LastName);
}
private Code code1;
}
下面是相應(yīng)的工作流標(biāo)記內(nèi)容。
<?Mapping XmlNamespace="ComponentModel" ClrNamespace="System.Workflow.ComponentModel" Assembly="System.Workflow.ComponentModel" ?> <?Mapping XmlNamespace="Compiler" ClrNamespace="System.Workflow.ComponentModel.Compiler" Assembly="System.Workflow.ComponentModel" ?> <?Mapping XmlNamespace="Activities" ClrNamespace="System.Workflow.Activities" Assembly="System.Workflow.Activities" ?> <?Mapping XmlNamespace="RuleConditions" ClrNamespace="System.Workflow.Activities.Rules" Assembly="System.Workflow.Activities.Rules" ?> <SequentialWorkflow x:Class="WorkflowWithParams.Workflow1" x:CompileWith="Workflow1.xoml.cs" ID="Workflow1" xmlns:x="Definition" xmlns="Activities"> <SequentialWorkflow.Parameters> <wcm:ParameterDeclaration Name="FirstName" Type="System.String" Direction="In" xmlns:wcm="ComponentModel" /> <wcm:ParameterDeclaration Name="LastName" Type="System.String" Direction="In" xmlns:wcm="ComponentModel" /> </SequentialWorkflow.Parameters> <Code ExecuteCode="code1_ExecuteCode" ID="code1" /> </SequentialWorkflow>
請(qǐng)注意,在創(chuàng)建該工作流的實(shí)例以便執(zhí)行時(shí),必須顯式初始化并傳入 Parameters 集合中靜態(tài)定義的所有參數(shù)。
工作流運(yùn)行庫(kù)
宿主通過(guò) WorkflowRuntime 類與 Windows Workflow Foundation 交互。請(qǐng)不要被如上所示示例宿主表面上的簡(jiǎn)單性所蒙騙,以至于您注意不到這一要點(diǎn)??梢宰屗拗髫?fù)責(zé)處理很多附加、關(guān)鍵的方面,例如:創(chuàng)建一個(gè)或多個(gè)進(jìn)程以及一個(gè)或多個(gè) AppDomain;按照需要封送 AppDomain 之間的調(diào)用;設(shè)置隔離機(jī)制。出于可伸縮性的原因,宿主可能需要?jiǎng)?chuàng)建多個(gè)進(jìn)程來(lái)利用一臺(tái)計(jì)算機(jī)中的多個(gè) CPU,或者在一個(gè)計(jì)算機(jī)場(chǎng)中運(yùn)行大量工作流實(shí)例。
宿主還可以做其他事情。例如,它可以控制在工作流需要長(zhǎng)久等待時(shí)應(yīng)用的策略,偵聽(tīng)特定事件并將它們傳達(dá)給用戶或管理員,設(shè)置超時(shí)并重試每個(gè)工作流,公開(kāi)性能計(jì)數(shù)器,以及寫入日志信息以用于調(diào)試和診斷。
主機(jī)通過(guò)在啟動(dòng)時(shí)向容器注冊(cè)的預(yù)定義和自定義服務(wù)來(lái)完成大多數(shù)附加任務(wù)。示例宿主沒(méi)有做上述任何一件事情,并且僅限于啟動(dòng)工作流實(shí)例。這在很多常見(jiàn)情況下是可以接受的。
工作流和活動(dòng)
讓我們后退一步,并且在工作流項(xiàng)目處于活動(dòng)狀態(tài)時(shí)分析一下 Visual Studio 2005 工具箱。圖 11 中顯示的工具箱列出了可用來(lái)設(shè)計(jì)步驟的順序及其相互關(guān)系,以便形成工作流模型的活動(dòng)。

表 2 提供每個(gè)活動(dòng)的簡(jiǎn)短說(shuō)明,以及這些活動(dòng)適用于哪些方案。
|
活動(dòng) |
說(shuō)明 |
|---|---|
|
Code |
使您能夠向工作流中添加 Microsoft Visual Basic .NET 或 C# 代碼以執(zhí)行自定義操作。但是,這些代碼不應(yīng)該用對(duì) Web 服務(wù)等外部資源的依賴性來(lái)阻塞工作流。 |
|
Compensate |
使您能夠在發(fā)生錯(cuò)誤時(shí)調(diào)用代碼來(lái)撤消或者補(bǔ)償已經(jīng)由工作流執(zhí)行的操作。通常,對(duì)于現(xiàn)在已被取消的操作,您可能希望向先前已經(jīng)獲得成功通知的用戶發(fā)送電子郵件。 |
|
ConditionedActivityGroup (CAG) |
使您的工作流能夠基于特定于每個(gè)活動(dòng)的準(zhǔn)則有條件地執(zhí)行一組子活動(dòng),直到針對(duì) CAG 整體滿足完成條件。子活動(dòng)相互獨(dú)立并可能并行執(zhí)行。 |
|
Delay |
使您能夠控制工作流的定時(shí)以及將延遲內(nèi)置到工作流。您可以在 Delay 活動(dòng)上提供超時(shí),以便工作流在恢復(fù)執(zhí)行之前暫停。 |
|
EventDriven |
代表一系列其執(zhí)行由事件觸發(fā)的活動(dòng)。第一個(gè)子活動(dòng)必須能夠等待外部事件。可行的首要子活動(dòng)是 EventSink 和 Delay。在這種情況下,Delay 用作超時(shí)。 |
|
EventSink |
在向 WorkflowRuntime 注冊(cè)的數(shù)據(jù)交換服務(wù)引發(fā)指定事件時(shí),使工作流能夠從該服務(wù)接收數(shù)據(jù)。 |
|
ExceptionHandler |
使您能夠處理指定類型的異常。ExceptionHandler 活動(dòng)是其他活動(dòng)的包裝,在指定的異常發(fā)生時(shí),這些活動(dòng)實(shí)際執(zhí)行所需的任何工作??筛鶕?jù)情況指定一個(gè)用于存儲(chǔ)異常的本地變量,并且使其可以在代碼隱藏中使用。 |
|
IfElse |
使您的工作流能夠有條件地執(zhí)行多個(gè)可供選擇的分支之一??稍诿總€(gè)分支上放置一個(gè)條件,而條件為真的第一個(gè)分支將執(zhí)行。無(wú)需在最后一個(gè)分支上放置條件,因?yàn)樗灰暈?#8220;else”分支。 |
|
InvokeMethod |
使您的工作流能夠調(diào)用接口上的方法,以便將消息從工作流發(fā)送到向 WorkflowRuntime 注冊(cè)的數(shù)據(jù)交換服務(wù)。 |
|
InvokeWebService |
使您的工作流能夠調(diào)用 Web 服務(wù)方法。您需要指定要使用的代理類(使用 WSDL),以及您想要調(diào)用的方法的名稱。同步和異步調(diào)用都受到支持。 |
|
InvokeWorkflow |
使您的工作流能夠調(diào)用或啟動(dòng)另一個(gè)工作流(可達(dá)到任意深度)。例如,被調(diào)用的工作流可以調(diào)用第三個(gè)工作流,該工作流又可以調(diào)用第四個(gè)工作流,等等。遞歸調(diào)用不受支持。受支持的調(diào)用模型是發(fā)后不理。 |
|
Listen |
使工作流能夠等待(可能存在的)多個(gè)事件之一,或者在指定的超時(shí)間隔之后停止等待,并且基于結(jié)果分支。可向每個(gè)分支中添加一個(gè)或多個(gè)由事件驅(qū)動(dòng)的活動(dòng)。只有第一個(gè)滿足條件的分支被執(zhí)行;其他分支都不會(huì)運(yùn)行。 |
|
Parallel |
使您的工作流能夠相互獨(dú)立地執(zhí)行兩個(gè)或更多個(gè)操作。該活動(dòng)在繼續(xù)執(zhí)行之前會(huì)等待這些操作終止。 |
|
Policy |
使您能夠表示或執(zhí)行規(guī)則集合。該活動(dòng)不在工具箱中;要訪問(wèn)它的功能,必須創(chuàng)建自定義活動(dòng)并使用派生。 |
|
Replicator |
使您的工作流能夠創(chuàng)建給定活動(dòng)的任意多個(gè)實(shí)例,并且順序或同時(shí)執(zhí)行它們。 |
|
SelectData |
使您的工作流能夠通過(guò)在外部數(shù)據(jù)源對(duì)象上定義的方法查詢外部數(shù)據(jù)。當(dāng)觸發(fā) SelectData 活動(dòng)時(shí),關(guān)聯(lián)的方法將在宿主線程內(nèi)部執(zhí)行。該方法返回的值被傳遞給工作流。 |
|
Sequence |
使您能夠協(xié)調(diào)一組子活動(dòng)的連續(xù)執(zhí)行。該序列在最后一個(gè)子活動(dòng)完成之后完成。 |
|
SetState |
使您的狀態(tài)機(jī)工作流能夠指定向新?tīng)顟B(tài)的轉(zhuǎn)換。 |
|
State |
表示狀態(tài)機(jī)工作流中的狀態(tài)。 |
|
StateInitialization |
在 State 活動(dòng)中,用作在狀態(tài)轉(zhuǎn)換時(shí)執(zhí)行的子活動(dòng)的容器。 |
|
Suspend |
掛起工作流的操作,以便能夠在發(fā)生某個(gè)錯(cuò)誤條件時(shí)進(jìn)行干預(yù)。當(dāng)工作流實(shí)例掛起時(shí),將記錄錯(cuò)誤??芍付ㄒ粋€(gè)消息字符串來(lái)幫助管理員診斷發(fā)生了什么事情。與當(dāng)前實(shí)例關(guān)聯(lián)的所有狀態(tài)信息都被保存,并且這些信息會(huì)在管理員繼續(xù)執(zhí)行時(shí)恢復(fù)。 |
|
Terminate |
使您能夠在發(fā)生任何異常情況時(shí)立即結(jié)束工作流的操作。如果是在 Parallel 活動(dòng)內(nèi)部調(diào)用,則所有分支都被突然終止,而無(wú)論它們的當(dāng)前狀態(tài)如何。當(dāng)工作流終止時(shí),會(huì)記錄錯(cuò)誤,并提供一個(gè)消息以幫助管理員弄清楚發(fā)生了什么事情。 |
|
Throw |
使您能夠引發(fā)指定類型的異常。使用該活動(dòng)等效于在用戶代碼中引發(fā)異常的代碼處理程序。該活動(dòng)是引發(fā) .NET 異常的聲明性方式。 |
|
TransactionalContext |
事務(wù)上下文 是用于對(duì)活動(dòng)進(jìn)行分組的塊。該活動(dòng)主要用于事務(wù)性執(zhí)行、補(bǔ)償和異常處理,可以根據(jù)情況進(jìn)行同步。通過(guò)同步事務(wù)性上下文,可確保對(duì)活動(dòng)中共享數(shù)據(jù)的任何訪問(wèn)都將正確地序列化。 |
|
UpdateData |
使您的工作流能夠通過(guò)在外部數(shù)據(jù)源對(duì)象上定義的方法更新數(shù)據(jù)存儲(chǔ)區(qū)。當(dāng) UpdateData 活動(dòng)被觸發(fā)時(shí),關(guān)聯(lián)的方法將在宿主線程內(nèi)部執(zhí)行。 |
|
WaitForData |
使您的工作流能夠從外部數(shù)據(jù)源對(duì)象接收信息。當(dāng)傳入的數(shù)據(jù)修改綁定數(shù)據(jù)源的狀態(tài)時(shí),該活動(dòng)被觸發(fā)。傳入的數(shù)據(jù)是通過(guò)綁定數(shù)據(jù)源服務(wù)接收的。 |
|
WaitForQuery |
使外部應(yīng)用程序能夠在您的工作流中查詢數(shù)據(jù)。該活動(dòng)將在從宿主收到查詢之前一直等待。來(lái)自外部應(yīng)用程序的查詢使用綁定數(shù)據(jù)源服務(wù)上的方法提交給工作流。 |
|
WebServiceReceive |
使作為 Web 服務(wù)本身公開(kāi)的工作流能夠接收 Web 服務(wù)請(qǐng)求。 |
|
WebServiceResponse |
使作為 Web 服務(wù)本身公開(kāi)的工作流能夠響應(yīng) Web 服務(wù)請(qǐng)求。 |
|
While |
使您的工作流能夠在一個(gè)條件被滿足時(shí)執(zhí)行一個(gè)或多個(gè)活動(dòng)。在每次迭代之前,都評(píng)估該條件。如果為真,則所有子活動(dòng)都會(huì)執(zhí)行;否則,該活動(dòng)完成??芍付暶餍詶l件或代碼條件。 |
活動(dòng)表示使用 Windows Workflow Foundation 進(jìn)行工作流編程的聲明性方法。使用活動(dòng),可在設(shè)計(jì)時(shí)創(chuàng)作工作流模型并將值分配給每個(gè)活動(dòng)的屬性。如果選擇帶有代碼分隔功能的工作流項(xiàng),則最后的結(jié)果會(huì)作為 XML 標(biāo)記保存到具有 .xoml 擴(kuò)展名的工作流標(biāo)記文件中。否則,創(chuàng)作的模型將作為對(duì)工作流對(duì)象模型的一系列調(diào)用持久保存在設(shè)計(jì)器生成的 C# 或 Visual Basic .NET 類文件中。前一種方法類似于 ASP.NET 頁(yè),而后一種方法類似于 Windows 窗體應(yīng)用程序所采用的方法。
Visual Studio 2005 隱藏了這兩種方法之間的大多數(shù)差異。您總是以可視方式設(shè)計(jì)工作流,并且 Visual Studio 2005 透明地將您的工作持久保存為兩種不同格式中的一種。如果您選擇采用“僅代碼”解決方案(沒(méi)有 XOML 和代碼分隔),則可調(diào)整設(shè)計(jì)器代碼以使其變得更加靈活一些。例如,可讓它從配置文件或數(shù)據(jù)庫(kù)中讀取參數(shù)的默認(rèn)值。如果選擇采用工作流標(biāo)記和代碼分隔,則在工作流的代碼及其模型之間產(chǎn)生巧妙的分隔。
是否可以用編程方式修改工作流模型?在設(shè)計(jì)時(shí),可在 Visual Studio 中以編程方式對(duì)工作流做您可以做的所有事情。在運(yùn)行時(shí),對(duì)活動(dòng)集合進(jìn)行動(dòng)態(tài)更新也是可以的,而這為您提供了對(duì)正在運(yùn)行的工作流實(shí)例進(jìn)行更改的能力。動(dòng)態(tài)更改由在設(shè)計(jì)時(shí)未知的業(yè)務(wù)更改激發(fā),或由首先修改然后完成業(yè)務(wù)過(guò)程的業(yè)務(wù)邏輯需要激發(fā)。在任何情況下,它都應(yīng)該只涉及有限的更改 — 完善而不是重新設(shè)計(jì)。
動(dòng)態(tài)更新適用于應(yīng)用程序上下文中的單個(gè)工作流實(shí)例。同一工作流類型的將來(lái)實(shí)例將不會(huì)受到更改的影響。對(duì)工作流實(shí)例的動(dòng)態(tài)更新可以從工作流實(shí)例本身中進(jìn)行,也可以從您的應(yīng)用程序代碼外部進(jìn)行。
Windows Workflow Foundation 框架支持 Web 服務(wù)互操作性,這包括能夠?qū)⒐ぷ髁髯鳛?Web 服務(wù)向 ASP.NET 客戶端和其他工作流公開(kāi)。Windows Workflow Foundation 支持將工作流作為在 Microsoft IIS 6.0 上運(yùn)行 ASP.NET 的 Web 服務(wù)器或服務(wù)器場(chǎng)上的 ASP.NET Web 服務(wù)公開(kāi)。
Windows Workflow Foundation 框架活動(dòng)集包含 WebServiceReceive 和 WebServiceResponse 活動(dòng),這使工作流能用作 Web 服務(wù)終結(jié)點(diǎn)。
要想作為 Web 服務(wù)公開(kāi),工作流必須包含 WebServiceReceive 活動(dòng),以便從客戶端獲得傳入的調(diào)用??旖莶藛蚊顚⒐ぷ髁髯鳛?Web 服務(wù)發(fā)布,如圖 12 所示。

開(kāi)發(fā)自定義活動(dòng)
Windows Workflow Foundation 中可擴(kuò)展性的要點(diǎn)是創(chuàng)建自定義活動(dòng),因?yàn)檫@使您可以擴(kuò)展用于生成工作流模型的構(gòu)造塊集。
讓我們研究一下活動(dòng)的內(nèi)部體系結(jié)構(gòu),方法是開(kāi)發(fā)一個(gè)自定義活動(dòng)來(lái)發(fā)送電子郵件。Windows Workflow Foundation 為自定義活動(dòng)提供一個(gè)現(xiàn)成的 Visual Studio 2005 模板。它的名稱為 Workflow Activity Library。該模板創(chuàng)建一個(gè)可任意重命名的 C# 文件 — 例如,可將其重命名為 SendMailActivity。活動(dòng)是從父類繼承的普通類??蓮娜魏维F(xiàn)有活動(dòng)(無(wú)論它是內(nèi)置的活動(dòng),還是您自己創(chuàng)建或從第三方供應(yīng)商購(gòu)買的活動(dòng))派生您的活動(dòng)。顯然,父類向新的組件中添加了預(yù)定義的行為。要完全從頭開(kāi)始生成活動(dòng),請(qǐng)讓其從 Activity 派生。下面的代碼示例顯示新類的主干。
public partial class SendMailActivity : System.Workflow.ComponentModel.Activity
{
public SendMailActivity()
{
InitializeComponent();
}
protected override Status Execute(ActivityExecutionContext context)
{
:
}
}
正如您可以猜到的那樣,Execute 方法是該組件的核心 — 即完成該組件的核心任務(wù)的位置。
在開(kāi)發(fā)之后,活動(dòng)就被放到工具箱中,以供拖放操作將其拖放到新的工作流應(yīng)用程序中。盡管屬性列表不是必需的,但不帶屬性的活動(dòng)幾乎沒(méi)有任何用處。要添加屬性,您需要在設(shè)計(jì)器中選擇正在開(kāi)發(fā)的活動(dòng),然后單擊 Properties 窗格上的 Activity Properties 項(xiàng)(參見(jiàn)圖 13)。

向活動(dòng)中添加屬性與向工作流中添加參數(shù)并無(wú)太大的不同。必須做的工作就是為每個(gè)需要的屬性配置名稱和屬性。圖 14 顯示如何向 SendMail 活動(dòng)中添加 To 屬性。

為了完成所有工作,我們添加了其他屬性(如 From、Subject、Body 和 Host),以便用戶可以完整地配置要發(fā)送的電子郵件。當(dāng)您添加屬性時(shí),向?qū)?huì)修改包含活動(dòng)的邏輯在內(nèi)的 C# 代碼隱藏文件。
最后一個(gè)步驟是使 Execute 方法變得充實(shí)一些,以指示它在執(zhí)行該活動(dòng)時(shí)發(fā)送電子郵件。
protected override Status Execute(ActivityExecutionContext context)
{
MailAddress toAddress = new MailAddress(To);
MailAddress fromAddress = new MailAddress(From);
MailAddressCollection addresses = new MailAddressCollection();
addresses.Add(toAddress);
MailMessage msg = new MailMessage(fromAddress, toAddress);
msg.Subject = Subject;
msg.Body = Body;
SmtpClient mail = new SmtpClient(Host);
mail.Send(msg);
return Status.Closed;
}
如果在工作流解決方案的內(nèi)部開(kāi)發(fā)活動(dòng)項(xiàng)目,則工作流文檔將自動(dòng)查找工具箱中列出的新活動(dòng),如圖 15 所示。否則,必須通過(guò)右鍵單擊工具箱來(lái)添加它。

圖 16 說(shuō)明 SendMail 活動(dòng)確實(shí)有效。

計(jì)劃更現(xiàn)實(shí)的工作流
讓我們看一下如何組合表 2 中列出的一些活動(dòng),從而解決一項(xiàng)更為現(xiàn)實(shí)的任務(wù)。假設(shè)有這樣一個(gè)業(yè)務(wù)應(yīng)用程序,其中,訂單在完成之前可能要經(jīng)歷多個(gè)狀態(tài)。在典型的方案中,有一些根據(jù)當(dāng)前狀態(tài)指示訂單中可能發(fā)生某些事件的規(guī)則。例如,可以處理或更新未完成的訂單,但不能將其取消或發(fā)送。
當(dāng)事件發(fā)生時(shí),狀態(tài)機(jī)工作流將轉(zhuǎn)換訂單的狀態(tài)。例如,當(dāng)訂單未完成并且 BeingProcessed 事件發(fā)生時(shí),狀態(tài)機(jī)工作流會(huì)將訂單轉(zhuǎn)換到正確的狀態(tài)。圖 17 顯示示例訂單狀態(tài)機(jī)工作流的關(guān)系圖。

讓我們首先創(chuàng)建一個(gè)狀態(tài)機(jī)工作流。您將使用 State 活動(dòng)對(duì)訂單的可能狀態(tài)進(jìn)行建模。然后,通過(guò)使用 EventDriven 活動(dòng)指定可以從每個(gè)狀態(tài)發(fā)生的事件。通過(guò)自定義服務(wù)產(chǎn)生的外部事件將轉(zhuǎn)換訂單的狀態(tài)。要執(zhí)行轉(zhuǎn)換,需要使用 SetState 活動(dòng)。創(chuàng)作工作流后,需要使用 Windows 窗體宿主應(yīng)用程序來(lái)檢驗(yàn)它。
工作流通過(guò)專門為此目的建立的服務(wù)與外部世界通信。該服務(wù)會(huì)引發(fā)工作流內(nèi)的事件驅(qū)動(dòng)活動(dòng)將掛鉤到的事件。同樣,該服務(wù)公開(kāi)了供該工作流調(diào)用的公共方法并向主機(jī)發(fā)送數(shù)據(jù)。方法和事件在接口中定義。該接口也稱為數(shù)據(jù)交換服務(wù)。每當(dāng)工作流與外部組件交互(進(jìn)行輸入和輸出)時(shí),您都需要該服務(wù)。
數(shù)據(jù)交換服務(wù)是常規(guī)的 .NET 類庫(kù),它最起碼包含一個(gè)接口定義以及一個(gè)實(shí)現(xiàn)該接口的類。該接口是為您希望表示的任務(wù)定制的。在此情況下,狀態(tài)機(jī)表示訂單的生存期,該接口包含五個(gè)事件。
[DataExchangeService]
public interface IOrderService
{
event EventHandler OrderCreated;
event EventHandler OrderShipped;
event EventHandler OrderUpdated;
event EventHandler OrderProcessed;
event EventHandler OrderCanceled;
}
[DataExchangeService] 屬性將 IOrderService 標(biāo)記為數(shù)據(jù)交換服務(wù)接口,以便工作流運(yùn)行庫(kù)知道它將用來(lái)與工作流實(shí)例交換數(shù)據(jù)。在此情況下,宿主將向一串 EventDriven 活動(dòng)引發(fā)事件,從而向工作流實(shí)例發(fā)送數(shù)據(jù)。如果需要,可通過(guò) InvokeMethod 活動(dòng)從工作流實(shí)例內(nèi)部調(diào)用 IOrderService 接口中的方法。
該接口中的事件聲明使用泛型,這是 .NET Framework 2.0 中的一項(xiàng)非常熱門的新功能。EventHandler 類是一個(gè)委托,它表示用于處理事件的函數(shù)的原型。在 .NET Framework 1.x 中,EventHandler 按如下方式定義。
void EventHandler(object sender, EventArgs e)
要使該事件傳遞自定義數(shù)據(jù)結(jié)構(gòu)(例如,OrderEventArgs),必須創(chuàng)建一個(gè)新的委托并使用它來(lái)替代 EventHandler。下面是一個(gè)示例。
delegate void OrderEventHandler(object sender, OrderEventArgs e)
該模式在 .NET Framework 2.0 中仍然有效。然而,.NET Framework 2.0 中泛型的出現(xiàn)使您無(wú)需顯式定義(和實(shí)例化)新的委托類即可獲得相同的結(jié)果。您將使用 EventHandler 委托的泛型版本,其中,事件數(shù)據(jù)的類型是參數(shù)。
事件傳遞 OrderEventArgs 類型的客戶端數(shù)據(jù),該類型是一個(gè)從 Windows Workflow Foundation WorkflowMessageEventArgs 類派生的自定義類,后者在同一個(gè)程序集中按如下方式定義。
[Serializable]
public class OrderEventArgs : WorkflowMessageEventArgs
{
private string _orderId;
public OrderEventArgs(Guid instanceId, string orderId) : base(instanceId)
{
_orderId = orderId;
}
public string OrderId
{
get { return _orderId; }
set { _orderId = value; }
}
}
下一步,需要定義一個(gè)實(shí)現(xiàn)該接口的類。該類所引發(fā)的公共方法與接口中引發(fā)的事件一樣多。
public class OrderService : IOrderService
{
public OrderService()
{
}
public void RaiseOrderCreatedEvent(string orderId, Guid instanceId)
{
if (OrderCreated != null)
OrderCreated(null, new OrderEventArgs(instanceId, orderId));
}
public void RaiseOrderShippedEvent(string orderId, Guid instanceId)
{
if (OrderShipped != null)
OrderShipped(null, new OrderEventArgs(instanceId, orderId));
}
public void RaiseOrderUpdatedEvent(string orderId, Guid instanceId)
{
if (OrderUpdated != null)
OrderUpdated(null, new OrderEventArgs(instanceId, orderId));
}
public void RaiseOrderProcessedEvent(string orderId, Guid instanceId)
{
if (OrderProcessed != null)
OrderProcessed(null, new OrderEventArgs(instanceId, orderId));
}
public void RaiseOrderCanceledEvent(string orderId, Guid instanceId)
{
if (OrderCanceled != null)
OrderCanceled(null, new OrderEventArgs(instanceId, orderId));
}
public event EventHandler OrderCreated;
public event EventHandler OrderShipped;
public event EventHandler OrderUpdated;
public event EventHandler OrderProcessed;
public event EventHandler OrderCanceled;
}
現(xiàn)在,需要用訂單服務(wù)編譯該程序集,并重新切換到狀態(tài)機(jī)工作流項(xiàng)目。在該工作流項(xiàng)目中,首先需要添加對(duì)新創(chuàng)建程序集的引用。接下來(lái),需要添加四個(gè) State 活動(dòng)并且按如下方式命名它們: WaitingForOrderState、OrderOpenState、OrderProcessedState、OrderCompletedState。
表 3 表示工作流的狀態(tài)關(guān)系圖。每個(gè)狀態(tài)都有一些能夠?qū)е孪蛄硪粋€(gè)狀態(tài)進(jìn)行轉(zhuǎn)換的事件。
|
狀態(tài) |
受支持的事件 |
轉(zhuǎn)換到 |
|---|---|---|
|
WaitingForOrderState |
OrderCreated |
OrderOpenState |
|
OrderOpenState |
OrderUpdated |
OrderOpenState |
|
OrderProcessed |
OrderProcessedState |
|
|
OrderProcessedState |
OrderUpdated |
OrderOpenState |
|
OrderCanceled |
Terminate 活動(dòng) |
|
|
OrderShipped |
OrderCompletedState |
|
|
OrderCompletedState |
要實(shí)現(xiàn)該關(guān)系圖,需要向每個(gè) State 活動(dòng)中添加與該表中受支持事件相同數(shù)目的 EventDriven 塊。例如,名為 WaitingForOrderState 的 State 活動(dòng)將包含單個(gè) EventDriven 活動(dòng)(該活動(dòng)的名稱可以是任意的,例如 OrderCreatedEvent)。如圖 18 所示,EventDriven 活動(dòng)嵌入一個(gè) EventSink 活動(dòng)和一個(gè) SetState 活動(dòng),以便捕獲外部事件并轉(zhuǎn)換到新的狀態(tài)。

在 EventSink 活動(dòng)的 Properties 窗格上,可選擇自己喜歡的數(shù)據(jù)交換服務(wù)(在此情況下為 IOrderService 接口)以及要預(yù)訂的事件名稱。如果單擊 EventSink 活動(dòng) Properties 窗格上的 InterfaceType 項(xiàng),則 Visual Studio 2005 將提供該項(xiàng)目可用的數(shù)據(jù)交換服務(wù)列表。選擇服務(wù)后,EventName 屬性反映由該服務(wù)所公開(kāi)事件的列表??蛇x擇自己感興趣的事件并繼續(xù)。對(duì)于 OrderCreatedEvent 活動(dòng),可選擇 OrderCreated 事件。
SetState 活動(dòng)將狀態(tài)機(jī)轉(zhuǎn)換到由其 TargetState 屬性指示的新?tīng)顟B(tài)。圖 18 中的 SetState 活動(dòng)設(shè)置為 OrderOpenState。
可對(duì)表 3 中的所有狀態(tài)和事件接收器重復(fù)執(zhí)行上述操作。最后,您的工作流應(yīng)該如圖 19 所示。

最后一個(gè)步驟涉及到生成 Windows 窗體應(yīng)用程序以測(cè)試該工作流。用戶界面包含一個(gè)用于跟蹤所有未完成訂單的列表視圖,以及用于創(chuàng)建新訂單的文本框和按鈕。其他按鈕將用來(lái)更新、處理和終止該訂單。
狀態(tài)機(jī)工作流在 Form_Load 事件中初始化。狀態(tài)機(jī)工作流的初始化要比順序工作流復(fù)雜一些,尤其是當(dāng)您希望能夠跟蹤狀態(tài)更改的時(shí)候。下面的代碼示例顯示如何初始化工作流運(yùn)行庫(kù)。
private void StartWorkflowRuntime()
{
// Create a new Workflow Runtime for this application
_runtime = new WorkflowRuntime();
// Register event handlers for the WorkflowRuntime object
_runtime.WorkflowTerminated += new
EventHandler(WorkflowRuntime_WorkflowTerminated);
_runtime.WorkflowCompleted += new
EventHandler(WorkflowRuntime_WorkflowCompleted);
// Create a new instance of the StateMachineTrackingService class
_stateMachineTrackingService = new StateMachineTrackingService(_runtime);
// Start the workflow runtime
_runtime.StartRuntime();
// Add a new instance of the OrderService to the runtime
_orderService = new OrderService();
_runtime.AddService(_orderService);
}
StateMachineTrackingService 在運(yùn)行庫(kù)之上工作,并且用跟蹤工作流中狀態(tài)更改的功能來(lái)擴(kuò)展它。數(shù)據(jù)交換服務(wù)的實(shí)例還被添加到運(yùn)行庫(kù)中。
當(dāng)用戶單擊以創(chuàng)建新訂單時(shí),將執(zhí)行下面的代碼。
private Guid StartOrderWorkflow(string orderID)
{
// Create a new GUID for the WorkflowInstanceId
Guid instanceID = Guid.NewGuid();
// Load the OrderWorkflows assembly
Assembly asm = Assembly.Load("OrderWorkflows");
// Get a type reference to the OrderWorkflows.Workflow1 class
Type workflowType = asm.GetType("OrderWorkflows.Workflow1");
// Start a new instance of the state machine with state tracking support
StateMachineInstance stateMachine =
_stateMachineTrackingService.RegisterInstance(workflowType, instanceID);
stateMachine.StateChanged += new
EventHandler(StateMachine_StateChanged);
stateMachine.StartWorkflow();
_stateMachineInstances.Add(instanceID.ToString(), stateMachine);
// Return the workflow GUID
return instanceID;
}
首先,代碼實(shí)例化工作流實(shí)例并注冊(cè)狀態(tài)更改的事件處理程序。請(qǐng)注意,使用 .NET Reflection 來(lái)獲得類型信息并不是絕對(duì)需要的,但這可以大大提高靈活性。普通的舊運(yùn)算符 typeof 也可以很好地將工作流實(shí)例的類型傳遞給工作流運(yùn)行庫(kù)。
圖 20 顯示正在工作的示例應(yīng)用程序。按鈕是基于所選工作流實(shí)例的狀態(tài)而啟用的。

當(dāng)用戶單擊給定按鈕時(shí),通信接口上的相應(yīng)事件將被引發(fā)并被工作流的事件接收器捕獲。例如,對(duì)處于掛起狀態(tài)的工作流實(shí)例而言,其 Order Processed 按鈕的單擊事件將按如下方式處理。
private void btnOrderEvent_Click(object sender, EventArgs e)
{
// Get the name of the clicked button
string buttonName = ((Button)sender).Name;
// Get the GUID of the selected order
Guid instanceID = GetSelectedWorkflowInstanceID();
// Get the ID of the selected order
string orderID = GetSelectedOrderID();
// Disable buttons before proceeding
DisableButtons();
// Determines what to do based on the name of the clicked button
switch(buttonName)
{
// Raise an OrderShipped event using the Order Local Service
case "btnOrderShipped":
_orderService.RaiseOrderShippedEvent(orderID, instanceID);
break;
// Raise an OrderUpdated event using the Order Local Service
case "btnOrderUpdated":
_orderService.RaiseOrderUpdatedEvent(orderID, instanceID);
break;
// Raise an OrderCanceled event using the Order Local Service
case "btnOrderCanceled":
_orderService.RaiseOrderCanceledEvent(orderID, instanceID);
break;
// Raise an OrderProcessed event using the Order Local Service
case "btnOrderProcessed":
_orderService.RaiseOrderProcessedEvent(orderID, instanceID);
break;
}
}
該工作流中引發(fā)的事件由圖 21 中所示的 EventDriven 活動(dòng)捕獲。

EventSink 活動(dòng)捕獲該事件,并且通過(guò)轉(zhuǎn)換到 SetState 活動(dòng)所設(shè)置的狀態(tài)來(lái)處理它。工作流中的狀態(tài)更改由附加的狀態(tài)跟蹤服務(wù)檢測(cè),并通過(guò) StateChanged 事件報(bào)告給宿主,如上述代碼清單所示。
您可以在 http://msdn.microsoft.com/workflow 找到本文中討論的全部示例的完整源代碼,以及更多的工作流內(nèi)容。
小結(jié)
Windows Workflow Foundation 旨在成為新的和現(xiàn)有的 Microsoft 產(chǎn)品的工作流框架,它向所有需要為 .NET 平臺(tái)創(chuàng)建工作流驅(qū)動(dòng)應(yīng)用程序的開(kāi)發(fā)人員提供了 WinFX 的強(qiáng)大功能和 Visual Studio 2005 的易用性。
Windows Workflow Foundation 為工作臺(tái)帶來(lái)的主要好處是統(tǒng)一的工作流模型和一組能夠取代很多專用庫(kù)的工具。在這方面,Windows Workflow Foundation 對(duì)于目前工作流產(chǎn)品的供應(yīng)商也具有重要意義,因?yàn)椴捎?Windows Workflow Foundation 則意味著他們不必再維護(hù)其低級(jí)別的代碼,并且可以集中力量去完成更高級(jí)別的任務(wù)。
Windows Workflow Foundation 是一種面向多種特定應(yīng)用程序和需要的工作流技術(shù)。Windows Workflow Foundation 因而成為一種廣泛的框架,它是為提高每個(gè)級(jí)別的可擴(kuò)展性而設(shè)計(jì)的。這種形式的可擴(kuò)展性的最佳示例是自定義活動(dòng)和可插接的運(yùn)行庫(kù)服務(wù)。自定義活動(dòng)使您可擴(kuò)展可用來(lái)創(chuàng)作工作流的構(gòu)造塊集??筛某志眯源鎯?chǔ)和跟蹤等運(yùn)行庫(kù)服務(wù)以適應(yīng)應(yīng)用程序的環(huán)境,并可使應(yīng)用程序?qū)?shù)據(jù)持久存儲(chǔ)到 Microsoft SQL Server 或其他供應(yīng)商的數(shù)據(jù)庫(kù)中。
Windows Workflow Foundation 的 Visual Studio 2005 擴(kuò)展將允許對(duì)工作流進(jìn)行可視化建模和直接代碼訪問(wèn)。
還可以在其他設(shè)計(jì)環(huán)境中承載可視化設(shè)計(jì)器,從而使設(shè)計(jì)器提供商可以將可視化建模功能嵌入到其自己的環(huán)境中,并且提供應(yīng)用程序用戶所熟悉的用戶體驗(yàn)。
本文僅僅討論了所有 Windows Workflow Foundation 技術(shù)和功能中的一些粗淺知識(shí),提供了有關(guān)其工作方式、內(nèi)部原理的概述和一些有代表性的示例代碼。
關(guān)于作者
Dino Esposito 是一位居住于意大利羅馬的培訓(xùn)師和顧問(wèn)。作為 Wintellect 團(tuán)隊(duì)的成員,Dino 專門研究 ASP.NET 和 ADO.NET,并且花費(fèi)大部分時(shí)間在歐洲和美國(guó)進(jìn)行教學(xué)和咨詢活動(dòng)。值得一提的是,Dino 為 Wintellect 管理 ADO.NET 課件,同時(shí)為 MSDN Magazine 撰寫 Cutting Edge 專欄。






