WF 入门 (第 1 部分)






4.89/5 (58投票s)
一系列关于 Microsoft NetFx3 的文章。从 WF 开始进行初步写作。
引言
随着 .NET Framework 3.0(以前称为 WinFX)的出现,Windows Presentation Foundation(以前称为 Avalon)、Communication Foundation(以前称为 Indigo)、Workflow Foundation(以前称为 WWF)和 Cardspace(以前称为 InfoCard)等术语随处可见。.NET Framework 3.0 还包含一种功能强大的通用对象初始化语言,称为 XAML(eXtensible Application Markup Language)。Microsoft 真正强调的是创建引人注目的用户体验以及跨边界的安全、无缝通信。
在本系列文章中,我将解释并深入探讨 .NET Framework 3.0 的一些核心组件。
让我们从 Windows Workflow Foundation (WF) 开始。
背景(可选)
我假设您具备 .NET Framework 的基本知识。我将尝试在概念出现时对其进行讲解。
工作流及其类型
在商业世界中,工作流就是一个业务流程(独立于技术),完成任务所必需。任何业务流程都可能涉及多个步骤,这些步骤可以是可选的,也可以是必需的。所以简单地说,“工作流”就是工作的流程。
如果再深入一点,“工作流”可以以**顺序**方式进行(即步骤一个接一个地执行),或者在流程中可能存在几个需要做出决策的点。由于流程无法在未满足某些条件的情况下从一个点移动到另一个点,因此我们有**基于状态**的工作流。
因此,我们可以简单地说有两种类型的工作流:
- Sequential
- 状态机
基于状态的工作流会等待外部实体执行某些操作,然后才能移至下一步。另一方面,顺序工作流只是一系列连续的操作,可能包含分支,但顺序工作流中的步骤不等待外部实体执行操作,然后继续。
在工作流中,当需要做出决定时会进行分支。工作流中的每个分支必须有两种选择:真或假。您不能在任何时候停止工作流来做出决定。
选择哪种工作流
顺序工作流遵循传统的思维方式——即,只要流程简单且在边界内,顺序工作流就能正常工作。
另一方面,状态机工作流处理工作流的不同状态。一个在最终成型之前涉及多个不同迭代的过程是状态机工作流的候选。
为什么使用工作流和 WF
创建工作流的最大原因之一是您实际上是在创建一个模型。大多数业务流程都有某种相关的模型,无论是用例、UML 图还是简单的流程图。在流程中,您始终都有某种流程,因此,在其执行过程中始终会涉及一些步骤。
有了 WF,模型和工作流就是一回事。您使用业务流程的各个部分来构建工作流,并且将工作流组合在一起,这就构成了模型。在 WF 之前,您会制作几张 UML 图、绘制流程图、编写伪代码、用项目符号文本解释操作应如何工作,最后才会编写代码,而在使用 WF 进行业务流程建模时,您也在构建应用程序!
Windows Workflow Foundation (WF)
如前所述,WF 是 Microsoft 新编程模型 .NET Framework 3.0 的一部分。它使业务流程能够以图形方式表达并直接链接到业务逻辑。通过 WF,工作流可以以以下方式表达:
- 声明性 XAML。
- 使用任何 .NET 目标语言的命令式代码。
- 通过 Visual Studio Designer 可视化开发。
WF 编程模型由一系列公开的 API 组成,这些 API 封装在 Microsoft .NET Framework 3.0 的名为 _System.Workflow_ 的命名空间中。
让我们快速看一下 WF 架构。
设计器
与命名空间交互最方便的方式当然是通过工作流设计器。您只需创建一个工作流,拖放活动,就完成了。一个完整的可工作模型!
活动
每个工作流都由一组活动(步骤/任务)组成。这些活动促进了业务流程或构成了业务流程的一部分。在 WF 的术语中,活动是执行工作流所需的实际工作单元。WF 允许您创建自己的自定义活动和自己的库,这将在下一篇文章中介绍。
运行时引擎
WF 的下一个组件是 WF 运行时引擎。WF 运行时引擎执行由 VS2005 Workflow Designer 创建的工作流。运行时引擎包含三个基本服务:
- 调度(安排工作流中活动的执行)
- 状态管理(允许持久化工作流的状态,而不是将其存储在数据库等其他机制中)
- 规则(执行策略活动,即在工作流的某些活动上添加规则以控制流程)
宿主进程
WF 没有可执行环境,需要一个宿主进程才能执行。它们本身不是应用程序,而是需要一个应用程序来托管。此宿主进程可以是 Windows 应用程序或 ASP.NET Web 应用程序。
安装
假设您已安装 VS 2005 和最新的 .NET Framework,要使用 WF,您只需下载并安装以下组件:
安装完组件后,在 VS 2005 中您将拥有新的项目类型,并能够导入和使用该命名空间。
图 01
开始使用 WF
Sequential Workflow Console Application 和 State Machine Workflow Console Application 为 WF 中可以创建的两种工作流类型提供了工作流模型:顺序和状态机。创建工作流应用程序的第一步是选择一个工作流模型,然后您需要向该模型添加活动。项目模板之间没有太大区别。控制台应用程序和库之间的区别仅在于控制台应用程序包含一个子 main 方法,该方法用于启动工作流。让我们看一下 WF 中各种可用项目模板的设计区域。
图 02
在深入探讨之前,理解 WF 中可用的各种活动非常重要。WF 提供了十几个基本活动集,我将在下面简要讨论它们,以便更好地理解 WF。
- 代码活动:代码活动允许您将自己的 VB 或 C# 代码添加到工作流中。代码活动中的实际代码位于“代码隐藏”中,并与工作流一起编译。
- 补偿活动:这是一种错误活动,只能添加到异常处理程序活动中。此活动用于在工作流发生错误时回滚所做的更改。您可以将其称为等同于回滚事务。
- 条件活动组:这是一种条件活动,它根据应用于组或附加到组的活动的某个条件来执行其他活动。
- 延迟活动:此活动允许您在工作流中构建基于间隔的暂停。您可以将其称为计时器活动,它将设置一个持续时间,以便工作流在继续执行之前暂停。
- 事件驱动活动:这是一种流程活动,其中包含在事件发生时要执行的其他活动。
- 故障处理程序活动:这是一个错误处理活动,就像您的代码中有
catch
块一样。当发生异常时,它可以包含各种触发的活动,包括补偿活动。 - IfElse 活动:这是一个条件活动,就像您的应用程序中有
If Else
块一样。您可以基于特定条件有一系列活动。 - 调用 Web 服务活动:此活动可以使用代理类调用 Web 服务的特定 Web 方法。它可以传递和接收参数。
- 侦听活动:此活动是一个复合活动。它需要至少两个事件驱动活动。
- 并行活动:这也是一个复合活动,需要至少两个顺序活动。此活动确保顺序活动的并行执行,并且在构成并行活动的所有顺序活动完成之前,它不会完成。
- 策略活动:此活动代表一组规则。规则具有条件和在满足条件时要采取的操作。它允许创建基于规则的工作流,而不是设置可能变得混乱的 IfElse 工作流。
- 复制器活动:这是一种条件活动。就像您的应用程序中有
For Each
语句一样。它实际上在运行时创建活动的多个实例,并且必须在复制器活动完成之前完成它们。 - 顺序活动:这也是一个复合活动,由多个顺序活动组成。它提供了一种方便的方式来链接那些需要按顺序执行的活动。
- 设置状态活动:这是一种流程活动,用于在状态机工作流中指定更改为新状态。
- 状态活动:这是一种流程活动。此活动在状态机工作流中代表一个状态。例如,当在状态机工作流中处理事件时,会插入一个不同的状态活动来处理该事件。
- 状态初始化活动:此活动是状态活动的一部分,由在状态活动初始化时要执行的其他活动组成。
- 挂起活动:这是一种流程活动,可以暂停工作流的操作,以便在工作流执行中引发需要特别关注的错误条件时进行干预。将记录与此活动相关的错误,并且暂停的工作流仍然可以接收消息,但这些消息会被排队。
- 终止活动:如果引发错误,此活动将立即结束工作流的执行。此活动还会记录错误,但与挂起活动不同,它会停止工作流的执行。
- 抛出活动:这是一个错误处理活动,就像您的应用程序中有
Throw
语句一样。您可以使用此活动将异常从一个工作流或活动抛出到另一个。 - 事务范围活动:此活动提供事务支持。构成事务的所有活动都放置在此活动中。
- While 活动:这是一种条件活动,就像我们应用程序中的
While
语句一样。它执行另一个活动,直到满足某个条件。条件可以是基于代码的,也可以是基于规则的。
工作流和活动
所有工作流和活动都是类。如果我们使用基本的 OOP 定义,一个编程语言类只不过是一个封装相关变量(属性)和方法(函数或存根)的现实世界构造。
如前所述,Workflow 命名空间是 Microsoft .NET Framework 3.0 中 System 命名空间下新添加的用于构建工作流应用程序的命名空间。实际上,有三个程序集构成了这个命名空间:Activities、ComponentModel 和 Runtime。您可以在 GAC 中找到它们的物理位置。
图 03
我无法详细解释这些命名空间中的每一个信息,您可以参考 MSDN。但是,为了给您一个概念,我们将简要讨论其中几个:
程序集 | 命名空间 | 描述 |
Workflow.Activities | Workflow.Activities | Activities 命名空间包含所有构成了 WF 设计器中可用活动类的类。 |
Activities.Rules | 此命名空间与 Policy Activity 一起工作,并封装了基于规则的工作流所需的所有类。 | |
Workflow.ComponentModel | ComponentModel | 此命名空间提供了用于创建工作流和活动的基类、接口和核心建模构造。 在 ComponentModel 命名空间下,Activity 类是工作流的基本构建块。 ![]() |
Workflow.Runtime | 此程序集为您的工作流提供运行时和托管环境。 |
揭开序幕
背景介绍完毕;是时候卷起袖子,看一些实际的代码示例了。
顺序工作流
打开您的 VS 2005,在文件 > 新建 > 项目 > 工作流下,您可以创建一个新的顺序工作流应用程序。当您在 C# 中创建一个新的工作流时,用于承载控制台应用程序的文件名为 _Program.cs_,而在 VB 中为 _Module1.vb_。
创建新项目后,我们删除了 _Workflow1.cs_ 文件,而是添加了一个新项来创建我们的顺序工作流。
图 04
在此澄清一下:WPF XAML 都是关于 UI 的,而 WF XAML 都是关于业务流程的。为了区分两者,引入了一个新的扩展名 XOML,它实际上是 eXtensible Object Markup Language(您可以称之为工作流标记)。如果我们仔细查看 XAML,它只是一个将 WPF .NET 类型序列化为 XML 的新格式。XAML 和 XOML 之间没有语法差异,只有语义不同。
将工作流文件添加到解决方案后,我们可以用 XML 编辑器或默认的工作流设计器打开它。在工作流设计器中,将代码活动拖放到顺序工作流的设计图面上。
图 05
在上图中您可以看到一个红色的感叹号。智能提示会告诉您 ExecuteCode 属性尚未设置,即您需要为您的活动添加一个处理程序。
转到属性,在“事件”部分双击 ExecuteCode 属性,就完成了。它会在您的代码隐藏文件中为您生成一个存根。您的代码现在将如下所示:
namespace _1SequentialWF
{
public partial class Workflow1 : SequentialWorkflowActivity
{
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
}
}
}
在此添加您想要执行的代码,例如,我们添加了一个简单的显示消息,它将在控制台输出窗口中打印“Hello Workflow!”。
图 06
您也可以在代码活动上设置断点。为了理解幕后到底发生了什么,让我们看一下 _Program.cs_ 文件,看看设计器为我们生成了什么代码。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
namespace _1SequentialWF
{
class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender,
WorkflowCompletedEventArgs e) {waitHandle.Set();};
workflowRuntime.WorkflowTerminated += delegate(object sender,
WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
typeof(_1SequentialWF.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
}
}
}
如果我们写出上述代码的伪代码,它看起来会是这样:
- Main 方法开始。
- 创建 WorkflowRutime 类的新实例。
- 添加处理程序。
- 创建 WorkflowInstance 类的实例。
- 将特定的工作流分配给实例。
- 执行工作流类构造函数,并设置工作流中所有活动的属性。
- 执行实例。
- Main 方法等待工作流中的所有活动完成执行。
- 现在处理每个活动,例如,在本例中是我们的代码活动的 ExecuteCode 处理程序被执行。
- Main 方法完成。
很有趣,不是吗?您只需添加一个简单的活动并为该活动定义了一个简单的任务,其余的都由系统自动为您处理。
将数据传递到工作流
许多需要实现的工作流之一可能需要将值传递到其中,例如已登录用户的 userID 等。工作流可以接受输入参数,也可以提供在工作流内部定义为属性的输出参数。
另一种将数据传递到工作流的方式是通过事件。通过事件,工作流作者添加一个接收事件及其关联数据的活动。
让我们修改之前的代码并引入属性。在这里,我们将通过添加对 _System.Windows.Forms_ 程序集的引用来在 Windows Form 应用程序中宿主我们的工作流应用程序。
图 07
namespace _1SequentialWF
{
public partial class Workflow1 : SequentialWorkflowActivity
{
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Hello Workflow! " + FirstName + " " + LastName);
System.Windows.Forms.MessageBox.Show("Hello Workflow! " + FirstName +
" " + LastName);
}
private string myFirstName;
public string FirstName
{
get { return myFirstName; }
set { myFirstName = value; }
}
private string myLastName;
public string LastName
{
get { return myLastName; }
set { myLastName = value; }
}
}
}
现在我们准备好从 Windows 应用程序启动我们的工作流应用程序。我们通过向解决方案添加一个新的 Windows 应用程序来做到这一点。
图 08
现在我们将我们的 Windows 应用程序设置为启动项目,并添加我们工作流应用程序的引用。
注意:在添加引用之前,必须将我们工作流应用程序的输出模式设置为类库,然后才能编译生成输出 dll。
图 09
添加对我们工作流项目的引用后,下一步是添加对 WF 系统程序集的引用。我们也将这样做!
图 10
在此之后,我们将通过在窗体上放置两个标签、两个文本框和一个按钮来设计我们的窗体,使其看起来像:
图 11
这是窗体的关联代码隐藏。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
namespace WindowsApplicationHost
{
public partial class Form1 : Form
{
private WorkflowRuntime wr;
public Form1()
{
InitializeComponent();
}
private void btnBeginWorkflow_Click(object sender, EventArgs e)
{
if (wr == null)
{
wr = new WorkflowRuntime();
wr.StartRuntime();
}
Dictionary<string,> parameters = new Dictionary<string,>();
parameters.Add("FirstName", txtFirstName.Text);
parameters.Add("LastName", txtLastName.Text);
WorkflowInstance instance = wr.CreateWorkflow(
typeof(_1SequentialWF.Workflow1), parameters);
instance.Start();
}
private void btnCloseWorkflow_Click(object sender, EventArgs e)
{
if (wr != null)
{
if (wr.IsStarted)
{
wr.StopRuntime();
MessageBox.Show("Workflow successfully stopped");
}
else
{
MessageBox.Show("Workflow not started yet");
}
}
}
}
}</string,></string,>
我们简单地添加了对 WF 命名空间的引用。在我们的主应用程序中,我们创建了 WorkflowRuntime 的一个实例,并使用了一个简单的 Dictionary 对象来保存用户的输入,然后将其传递给我们的工作流。
以下是我们应用程序的输出:
图 12
图 13
这样,我们就成功地在 Windows 窗体应用程序中宿主了我们的工作流应用程序,并且还向我们的工作流传递了值。
在接下来的文章中,我将详细介绍如何附加各种活动、声明基于规则的工作流,以及对状态机工作流和活动库的详细见解,然后是 Microsoft .NET Framework 3.0 的其余组件。
编码愉快~