本主题是 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 用于承载工作流。 在
usingProgram.csusingModule1.vb 顶部的现有 using 或 Imports 语句下面,添加以下 或 Imports 语句。Imports NumberGuessWorkflowActivities Imports System.Threadingusing 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每次工作流变为空闲状态等待下一个猜测时,都会调用此处理程序并设置
idleActionAutoResetEvent。 下面步骤中的代码使用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