Windows Workflow Foundation (WF) 的一个主要功能就是运行时能够在数据库中持久保存和卸载空闲的工作流。 本文的主要目的是介绍工作流的持久化,在文章的第一部分会先创建一个猜数字的工作流实例,程序生成一个20以内的随机数,用户输入所猜测的值,程序进行相应的提示。为了体现持久化方式的不同,在第一部分中,会采用内存中保存的工作流实例来演示。
然后在第二部分里面,当工作流处于空闲状态时会保存工作流实例到数据库,然后在需要的时刻从数据库中加载之前保存的实例数据,继续运行工作流。
本文引用了 http://msdn.microsoft.com/zh-cn/library/dd489453.aspx 中的部分代码。整个项目的结构如下: 包括一个Activity Library工作流活动库NumberGuessWorkflowActivities和一个控制台应用程序作为工作流宿主。
第一步:使用托管代码创建一个代码活动ReadInt,在其中我们使用Bookmark创建一个暂停点,用以接收用户输入的猜测值。
1、在NumberGuessWorkflowActivities中添加代码活动ReadInt
public sealed class ReadInt : NativeActivity<int>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
// 如果活动返回值,则从 CodeActivity<TResult>
// 派生并从 Execute 方法返回该值。
protected override void Execute(NativeActivityContext context)
{
//获取运行时指定的书签名
string name = BookmarkName.Get(context);
if (name == string.Empty)
{
throw new ArgumentException("BookmarkName cannot be an Empty string.", "BookmarkName");
}
//创建一个点,以便 System.Activities.NativeActivity 能够在该点被动等待恢复,该点具有指定名称,
//还提供在通知恢复操作完成时要执行的指定方法。
context.CreateBookmark(name, new BookmarkCallback(OnReadComplete));
}
//指示活动是否会使工作流进入空闲状态。必须返回true,因为我们的持久化工作将会在工作流处于PersistentIdle状态下进行
protected override bool CanInduceIdle
{
get { return true; }
}
//state参数的值来自WorkflowApplication.ResumeBookmark方法提供的参数。本例中用来保存用户输入的猜测值
void OnReadComplete(NativeActivityContext context, Bookmark bookmark, object state)
{
//为活动的自变量【Result】赋值
this.Result.Set(context, Convert.ToInt32(state));
}
}
备注:
1、默认情况下,代码活动继承于CodeActivity类,而本例中我们使用的是NativeActivity<TResult>,因为CodeActivity<TResult> 并不支持使用书签。 NativeActivity 是一个带有受保护的构造函数的抽象类。 与 CodeActivity 一样, NativeActivity 用于通过实现Execute(NativeActivityContext) 方法来编写命令性行为。 与 CodeActivity 不同的是,通过传递给 Execute(NativeActivityContext) 方法的 NativeActivityContext 对象, NativeActivity 有权访问工作流运行时的所有公开的功能。
2、在使用ReadInt之前需要重新生成解决方案。
第二步: 创建状态机工作流
创建工作流
-
在“解决方案资源管理器”中,右击“NumberGuessWorkflowActivities”,选择“添加”,然后选择“新建项”。
-
在“已安装”、“常规项”节点中,选择“工作流”。从“工作流”列表中选择“活动”。
-
在“名称”框中键入 StateMachineNumberGuessWorkflow,然后单击“添加”。
-
从“工具箱”的“状态机”一节中,拖动StateMachine 活动,并将其拖到工作流设计图面上的“在此处拖动活动”标签。
创建工作流变量和参数
-
如果设计器中尚未显示工作流,请在“解决方案资源管理器”中双击StateMachineNumberGuessWorkflow.xaml,使其显示在设计器中。
-
单击工作流设计器左下方的“参数”,以显示“参数”窗格。
-
单击“创建参数”。
-
在“名称”框中键入 MaxNumber,从“方向”下拉列表中选择“输入”,再从“参数类型”下拉列表中选择“Int32”,然后按 Enter 保存该参数。
-
单击“创建参数”。
-
在新添加的 MaxNumber 参数下方的“名称”框中键入Turns,从“方向”下拉列表中选择“输出”,再从“参数类型”下拉列表中选择“Int32”,然后按 Enter。
-
单击活动设计器左下方的“参数”,以关闭“参数”窗格。
-
单击工作流设计器左下方的“变量”,以显示“变量”窗格。
-
单击“创建变量”。
-
在“名称”框中键入 Guess,从“变量类型”下拉列表中选择“Int32”,然后按 Enter 保存该变量。
-
单击“创建变量”。
-
在“名称”框中键入 Target,从“变量类型”下拉列表中选择“Int32”,然后按 Enter 保存该变量。
-
单击活动设计器左下方的“变量”,以关闭“变量”窗格。
添加工作流活动
-
单击 State 将其选中。在“属性窗口”中,将DisplayName 更改为“启动目标”。
-
在工作流设计器中双击刚刚重命名“初始化目标”状态,以将其展开。
-
将“Assign”活动从“工具箱”的“基元”部分拖放至状态部分的“输入”活动下。在“到”框中键入“目标”,并将以下表达式键入“输入 C# 表达式”或“输入 VB 表达式”框。
new System.Random().Next(1, MaxNumber + 1)
-
通过单击工作流设计器顶部的痕迹显示中的 StateMachine,返回到工作流设计器中的整体状态机视图。
-
从“工具箱”的“状态机”部分将“状态”活动拖动到工作流设计器上,并将鼠标悬停在“初始化目标”状态上。请注意,新状态结束时,“初始化目标”状态周围会出现四个三角形。将新状态放到“初始化目标”状态下面紧邻的三角形上。此操作将新状态放置到工作流,并创建从“初始化目标”状态到新的状态的转移。
-
单击“State”以选择它,将 DisplayName 更改为“输入 Guess”,然后双击工作流设计器中的状态,以将其展开。
-
将“ReadInt”活动从“工具箱”的“基元”部分拖放至状态部分的“输入”活动下。
-
将以下表达式键入“WriteLine”的“文本”属性框中。"Please enter a number between 1 and " + MaxNumber
-
将“Assign”活动从“工具箱”的“基元”部分拖放至状态部分的“退出”活动下。
-
在“To”框中键入“Turns”,并将Turns + 1 键入“输入 C# 表达式”或“输入 VB 表达式”框。
-
通过单击工作流设计器顶部的痕迹显示中的 StateMachine,返回到工作流设计器中的整体状态机视图。
-
将“FinalState”活动从“工具箱”的“状态机”部分,将鼠标悬停在“输入 Guess” 状态,并将其放到“输入 Guess”状态右侧显示的三角形上,以便在“输入 Guess”和“最终状态”之间创建转换。
-
转换的默认实例名称为 T2。单击工作流设计器中的转换并选中它,并将其DisplayName 设置为“猜想正确”。然后单击并选择“最终状态”,并向右拖动,以便有显示完全转换名称的空间,而无需覆盖这二个状态中的一个。这将使它易于完成本教程中的剩余步骤。
-
在工作流设计器中双击刚刚重命名“猜想正确”转换,以将其展开。
-
将“WriteLine”活动从“工具箱”的NumberGuessWorkflowActivities 部分拖放至转换部分的“触发器”下。
-
在 ReadInt 活动的“属性窗口”中,将包括引号的EnterGuess键入BookmarkName 属性值框中,并将“猜想”键入“结果”属性值框中
-
将以下表达式键入“猜想正确”转换的 Condition 属性值框中。Guess == Target
-
通过单击工作流设计器顶部的痕迹显示中的 StateMachine,返回到工作流设计器中的整体状态机视图。
-
根据猜测是否正确,工作流应转换到“最终状态” 或返回到另一尝试的“输入猜想”状态中。这两个转换共享同一触发器,等待用户通过ReadInt 活动接收猜想。这称为共享转换。要创建共享转换,请单击圆圈,以指示“猜想正确”转换的开始,并将它拖动到所需的状态。本事例中的转换属自行转换,因此拖动“猜想正确”转换的起始点并将其放回“输入猜想”的底部。创建转换后,在工作流设计器中它并将其DisplayName 属性设置为“猜想不正确”。
-
将以下表达式键入“条件”框属性值框。Guess != Target
-
将“If”活动从“工具箱”的“控制流”部分拖放至转换部分的“操作”下。
-
将以下表达式键入“If”“活动的”“Condition”属性值框。Guess < Target
-
将两个“WriteLine”活动从“工具箱”的“基元”部分进行拖放,以便一个在“If”活动的“Then”部分中,一个在“Else”部分中。
-
单击“Then”部分中的“WriteLine”活动将其选中,然后将以下表达式键入“文本”属性值框中。"Your guess is too low."
-
单击“Else”部分中的“WriteLine”活动将其选中,然后将以下表达式键入“文本”属性值框中。"Your guess is too high."
-
通过单击工作流设计器顶部的痕迹显示中的 StateMachine,返回到工作流设计器中的整体状态机视图。
下面的示例阐释已完成的工作流。
三个状态State
两个Transition