如何:运行工作流
本主题是 Windows Workflow Foundation 入门教程的延续,讨论如何创建工作流宿主并运行上一主题 How to: Create a Workflow 中定义的工作流。
注意
入门教程中的每个主题都依赖于前面的主题。 若要完成本主题,必须先完成 How to: Create an Activity 和 How to: Create a Workflow。
创建工作流宿主项目
使用 Visual Studio 2012 从上个主题如何:创建活动中打开解决方案。
在 解决方案资源管理器 中右键单击 WF45GettingStartedTutorial 解决方案,选择 “添加”和 “新建项目”。
提示
如果未显示“解决方案资源管理器”窗口,请从“视图”菜单中选择“解决方案资源管理器”。
在 “已安装” 节点中,选择 “Visual C#”、 “工作流” (或 “Visual Basic”、 “工作流”)。
注意
Visual C# 或 Visual Basic 节点可能位于“已安装”节点中的“其他语言”节点下,具体取决于哪种编程语言被配置为 Visual Studio 中的主语言。
确保在 .NET Framework 版本下拉列表中选择“.NET Framework 4.5”。 从 “工作流” 列表中选择 “工作流控制台应用程序” 。 在“名称”框中键入
NumberGuessWorkflowHost
,然后单击“确定”。 这将创建适合初学者的工作流应用程序,它具备基本的工作流承载支持。 基本承载代码将修改用于运行工作流应用程序。在 解决方案资源管理器 中右键单击新添加的 NumberGuessWorkflowHost ,然后选择 “添加引用”。 在 “添加引用” 列表中选择 “解决方案” ,选中 NumberGuessWorkflowActivities旁边的复选框,然后单击 “确定”。
在 解决方案资源管理器 中右键单击 Workflow1.xaml ,然后选择 “删除”。 单击“确定”以确认。
修改工作流承载代码
在 解决方案资源管理器 中双击 “Program.cs” 或 “Module1.vb” ,以显示其代码。
提示
如果未显示“解决方案资源管理器”窗口,请从“视图”菜单中选择“解决方案资源管理器”。
因为此项目是用 “工作流控制台应用程序” 模板创建的,所以 “Program.cs” 或 “Module1.vb” 包含以下基本工作流承载代码。
' Create and cache the workflow definition. Dim workflow1 As Activity = New Workflow1() WorkflowInvoker.Invoke(workflow1)
// Create and cache the workflow definition. Activity workflow1 = new Workflow1(); WorkflowInvoker.Invoke(workflow1);
生成的承载代码使用 WorkflowInvoker。 WorkflowInvoker 提供一种简单工作流调用方法,就像方法调用一样,仅可用于不使用持久性的工作流。 WorkflowApplication 为执行工作流(包括生命周期事件通知、执行控制、书签恢复和持久性)提供更丰富的模型。 此示例使用书签并且将 WorkflowApplication 用于承载工作流。 在
using
Program.csusing
Module1.vb 顶部的现有 using 或 Imports 语句下面,添加以下 或 Imports 语句。Imports NumberGuessWorkflowActivities Imports System.Threading
using NumberGuessWorkflowActivities; using System.Threading;
用以下基本 WorkflowInvoker 承载代码替换使用 WorkflowApplication 的代码行。 此示例承载代码演示承载和调用工作流的基本步骤,但尚不包含用于成功运行本主题中的工作流的功能。 在以下步骤中,将修改此基本代码并添加其他功能,直到完成应用程序。
注意
你必须将以下示例中的
Workflow1
替换为FlowchartNumberGuessWorkflow
、SequentialNumberGuessWorkflow
或StateMachineNumberGuessWorkflow
(取决于在上一步骤如何:创建工作流中完成的工作流)。 如果不替换Workflow1
,在尝试生成或运行工作流时会出现生成错误。AutoResetEvent syncEvent = new AutoResetEvent(false); WorkflowApplication wfApp = new WorkflowApplication(_wf); wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e) { syncEvent.Set(); }; wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e) { Console.WriteLine(e.Reason); syncEvent.Set(); }; wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e) { Console.WriteLine(e.UnhandledException.ToString()); return UnhandledExceptionAction.Terminate; }; wfApp.Run(); syncEvent.WaitOne();
Dim syncEvent As New AutoResetEvent(False) Dim wfApp As New WorkflowApplication(New Workflow1()) wfApp.Completed = Sub(e As WorkflowApplicationCompletedEventArgs) syncEvent.Set() End Sub wfApp.Aborted = Sub(e As WorkflowApplicationAbortedEventArgs) Console.WriteLine(e.Reason) syncEvent.Set() End Sub wfApp.OnUnhandledException = Function(e As WorkflowApplicationUnhandledExceptionEventArgs) Console.WriteLine(e.UnhandledException) Return UnhandledExceptionAction.Terminate End Function wfApp.Run() syncEvent.WaitOne()
此代码将创建一个 WorkflowApplication,订阅三个工作流生命周期事件,通过调用 Run来启动工作流,然后等待工作流完成。 工作流完成时,将设置 AutoResetEvent ,并且宿主应用程序也完成。
设置工作流的输入参数
在 “Program.cs” 或 “Module1.vb” 顶部的现有
using
或Imports
语句下面,添加以下语句。将创建新 WorkflowApplication 的代码行替换为以下代码,该代码创建一个参数字典并在创建后将其传递给工作流。
注意
请将以下示例中的
Workflow1
替换为FlowchartNumberGuessWorkflow
和SequentialNumberGuessWorkflow
和 orStateMachineNumberGuessWorkflow
和 depending on which workflow you completed in the previousWorkflow1
中完成的工作流)。 如果不替换Workflow1
,在尝试生成或运行工作流时会出现生成错误。var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } }; WorkflowApplication wfApp = new(_wf, inputs) {
Dim inputs As New Dictionary(Of String, Object) inputs.Add("MaxNumber", 100) Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
此字典包含一个键为
MaxNumber
的元素。 输入字典中的键对应工作流根活动的输入参数。 工作流使用MaxNumber
来确定随机生成数的上边界。
检索工作流的输出参数
修改 Completed 处理程序以检索并显示工作流使用的轮数。
Completed = delegate (WorkflowApplicationCompletedEventArgs e) { int Turns = Convert.ToInt32(e.Outputs["Turns"]); Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns); syncEvent.Set(); },
wfApp.Completed = Sub(e As WorkflowApplicationCompletedEventArgs) Dim Turns As Integer = Convert.ToInt32(e.Outputs("Turns")) Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns) syncEvent.Set() End Sub
继续执行书签
将以下代码添加到
Main
方法顶部,紧接在现有 AutoResetEvent 声明之后。AutoResetEvent idleEvent = new AutoResetEvent(false);
Dim idleEvent As New AutoResetEvent(False)
将以下 Idle 处理程序添加到
Main
中,紧接在现有的三个工作流生命周期处理程序的下面。Idle = delegate (WorkflowApplicationIdleEventArgs e) { idleEvent.Set(); } };
wfApp.Idle = Sub(e As WorkflowApplicationIdleEventArgs) idleEvent.Set() End Sub
每次工作流变为空闲状态等待下一个猜测时,都会调用此处理程序并设置
idleAction
AutoResetEvent。 下面步骤中的代码使用idleEvent
和syncEvent
来确定工作流是在等待下一个猜测还是已完成。移除对
WaitOne
的调用,并替换为收集用户输入并恢复 Bookmark的代码。移除下面的代码行。
syncEvent.WaitOne();
syncEvent.WaitOne()
使用下面的示例替换它。
// Loop until the workflow completes. WaitHandle[] handles = new WaitHandle[] { syncEvent, idleEvent }; while (WaitHandle.WaitAny(handles) != 0) { // Gather the user input and resume the bookmark. bool validEntry = false; while (!validEntry) { if (!Int32.TryParse(Console.ReadLine(), out int Guess)) { Console.WriteLine("Please enter an integer."); } else { validEntry = true; wfApp.ResumeBookmark("EnterGuess", Guess); } } }
' Loop until the workflow completes. Dim waitHandles As WaitHandle() = New WaitHandle() {syncEvent, idleEvent} Do While WaitHandle.WaitAny(waitHandles) <> 0 'Gather the user input and resume the bookmark. Dim validEntry As Boolean = False Do While validEntry = False Dim Guess As Integer If Int32.TryParse(Console.ReadLine(), Guess) = False Then Console.WriteLine("Please enter an integer.") Else validEntry = True wfApp.ResumeBookmark("EnterGuess", Guess) End If Loop Loop
生成并运行应用程序
在 解决方案资源管理器 中,右键单击 NumberGuessWorkflowHost 项目,然后选择 “设为启动项目”。
按 Ctrl+F5 生成并运行应用程序。 尝试以尽可能少的次数猜出该数。
要尝试其他工作流样式的应用程序,请用
Workflow1
、 WorkflowApplication 或FlowchartNumberGuessWorkflow
(取决于所需工作流样式)替换代码中用于创建SequentialNumberGuessWorkflow
的StateMachineNumberGuessWorkflow
。var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } }; WorkflowApplication wfApp = new(_wf, inputs) {
Dim inputs As New Dictionary(Of String, Object) inputs.Add("MaxNumber", 100) Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
有关如何向工作流应用程序添加持久性的说明,请参阅下一主题 How to: Create and Run a Long Running Workflow。
示例
下面的示例是 Main
方法的完整代码清单。
注意
请将以下示例中的 Workflow1
替换为 FlowchartNumberGuessWorkflow
和 SequentialNumberGuessWorkflow
和 or StateMachineNumberGuessWorkflow
和 depending on which workflow you completed in the previous Workflow1
中完成的工作流)。 如果不替换 Workflow1
,在尝试生成或运行工作流时会出现生成错误。
static void Main(string[] args)
{
AutoResetEvent syncEvent = new AutoResetEvent(false);
AutoResetEvent idleEvent = new AutoResetEvent(false);
var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };
WorkflowApplication wfApp = new(_wf, inputs)
{
Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
int Turns = Convert.ToInt32(e.Outputs["Turns"]);
Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns);
syncEvent.Set();
},
Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
Console.WriteLine(e.Reason);
syncEvent.Set();
},
OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine(e.UnhandledException.ToString());
return UnhandledExceptionAction.Terminate;
},
Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
idleEvent.Set();
}
};
wfApp.Run();
// Loop until the workflow completes.
WaitHandle[] handles = new WaitHandle[] { syncEvent, idleEvent };
while (WaitHandle.WaitAny(handles) != 0)
{
// Gather the user input and resume the bookmark.
bool validEntry = false;
while (!validEntry)
{
if (!Int32.TryParse(Console.ReadLine(), out int Guess))
{
Console.WriteLine("Please enter an integer.");
}
else
{
validEntry = true;
wfApp.ResumeBookmark("EnterGuess", Guess);
}
}
}
}
Sub Main()
Dim syncEvent As New AutoResetEvent(False)
Dim idleEvent As New AutoResetEvent(False)
Dim inputs As New Dictionary(Of String, Object)
inputs.Add("MaxNumber", 100)
Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
wfApp.Completed =
Sub(e As WorkflowApplicationCompletedEventArgs)
Dim Turns As Integer = Convert.ToInt32(e.Outputs("Turns"))
Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns)
syncEvent.Set()
End Sub
wfApp.Aborted =
Sub(e As WorkflowApplicationAbortedEventArgs)
Console.WriteLine(e.Reason)
syncEvent.Set()
End Sub
wfApp.OnUnhandledException =
Function(e As WorkflowApplicationUnhandledExceptionEventArgs)
Console.WriteLine(e.UnhandledException)
Return UnhandledExceptionAction.Terminate
End Function
wfApp.Idle =
Sub(e As WorkflowApplicationIdleEventArgs)
idleEvent.Set()
End Sub
wfApp.Run()
' Loop until the workflow completes.
Dim waitHandles As WaitHandle() = New WaitHandle() {syncEvent, idleEvent}
Do While WaitHandle.WaitAny(waitHandles) <> 0
'Gather the user input and resume the bookmark.
Dim validEntry As Boolean = False
Do While validEntry = False
Dim Guess As Integer
If Int32.TryParse(Console.ReadLine(), Guess) = False Then
Console.WriteLine("Please enter an integer.")
Else
validEntry = True
wfApp.ResumeBookmark("EnterGuess", Guess)
End If
Loop
Loop
End Sub