如何:运行工作流How to: Run a Workflow

本主题是 Windows Workflow Foundation 入门教程的延续,讨论如何创建工作流宿主并运行上一主题 How to: Create a Workflow 中定义的工作流。This topic is a continuation of the Windows Workflow Foundation Getting Started tutorial and discusses how to create a workflow host and run the workflow defined in the previous How to: Create a Workflow topic.

备注

入门教程中的每个主题都依赖于前面的主题。Each topic in the Getting Started tutorial depends on the previous topics. 若要完成本主题,必须先完成 How to: Create an ActivityHow to: Create a WorkflowTo complete this topic you must first complete How to: Create an Activity and How to: Create a Workflow.

备注

若要下载完整版教程,请参阅 Windows Workflow Foundation (WF45) — 入门教程To download a completed version of the tutorial, see Windows Workflow Foundation (WF45) - Getting Started Tutorial.

创建工作流宿主项目To create the workflow host project

  1. 使用 Visual Studio 2012 打开先前如何:创建活动主题中的解决方案。Open the solution from the previous How to: Create an Activity topic by using Visual Studio 2012.

  2. 解决方案资源管理器 中右键单击 WF45GettingStartedTutorial 解决方案,选择 “添加”“新建项目”Right-click the WF45GettingStartedTutorial solution in Solution Explorer and select Add, New Project.

    提示

    如果未显示 “解决方案资源管理器” 窗口,请从 “视图” 菜单中选择 “解决方案资源管理器”If the Solution Explorer window is not displayed, select Solution Explorer from the View menu.

  3. “已安装” 节点中,选择 “Visual C#”“工作流” (或 “Visual Basic”“工作流”)。In the Installed node, select Visual C#, Workflow (or Visual Basic, Workflow).

    备注

    Visual C#Visual Basic 节点可能位于 “已安装” 节点中的 “其他语言” 节点下,具体取决于哪种编程语言被配置为 Visual Studio 中的主语言。Depending on which programming language is configured as the primary language in Visual Studio, the Visual C# or Visual Basic node may be under the Other Languages node in the Installed node.

    确保在 .NET Framework 版本下拉列表中选择 “.NET Framework 4.5”Ensure that .NET Framework 4.5 is selected in the .NET Framework version drop-down list. “工作流” 列表中选择 “工作流控制台应用程序”Select Workflow Console Application from the Workflow list. NumberGuessWorkflowHost在 "名称" 框中键入,然后单击 "确定"Type NumberGuessWorkflowHost into the Name box and click OK. 这将创建适合初学者的工作流应用程序,它具备基本的工作流承载支持。This creates a starter workflow application with basic workflow hosting support. 基本承载代码将修改用于运行工作流应用程序。This basic hosting code is modified and used to run the workflow application.

  4. 解决方案资源管理器 中右键单击新添加的 NumberGuessWorkflowHost ,然后选择 “添加引用”Right-click the newly added NumberGuessWorkflowHost project in Solution Explorer and select Add Reference. “添加引用” 列表中选择 “解决方案” ,选中 NumberGuessWorkflowActivities旁边的复选框,然后单击 “确定”Select Solution from the Add Reference list, check the checkbox beside NumberGuessWorkflowActivities, and then click OK.

  5. 解决方案资源管理器 中右键单击 Workflow1.xaml ,然后选择 “删除”Right-click Workflow1.xaml in Solution Explorer and choose Delete. 单击 “确定” 进行确认。Click OK to confirm.

修改工作流承载代码To modify the workflow hosting code

  1. 解决方案资源管理器 中双击 “Program.cs”“Module1.vb” ,以显示其代码。Double-click Program.cs or Module1.vb in Solution Explorer to display the code.

    提示

    如果未显示 “解决方案资源管理器” 窗口,请从 “视图” 菜单中选择 “解决方案资源管理器”If the Solution Explorer window is not displayed, select Solution Explorer from the View menu.

    因为此项目是用 “工作流控制台应用程序” 模板创建的,所以 “Program.cs”“Module1.vb” 包含以下基本工作流承载代码。Because this project was created by using the Workflow Console Application template, Program.cs or Module1.vb contains the following basic workflow hosting code.

    ' 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);
    

    生成的承载代码使用 WorkflowInvokerThis generated hosting code uses WorkflowInvoker. WorkflowInvoker 提供一种简单工作流调用方法,就像方法调用一样,仅可用于不使用持久性的工作流。WorkflowInvoker provides a simple way for invoking a workflow as if it were a method call and can be used only for workflows that do not use persistence. WorkflowApplication 为执行工作流(包括生命周期事件通知、执行控制、书签恢复和持久性)提供更丰富的模型。WorkflowApplication provides a richer model for executing workflows that includes notification of life-cycle events, execution control, bookmark resumption, and persistence. 此示例使用书签并且将 WorkflowApplication 用于承载工作流。This example uses bookmarks and WorkflowApplication is used for hosting the workflow. using Program.cs Module1.vb 顶部的现有 using Imports 语句下面,添加以下Imports 语句。Add the following using or Imports statement at the top of Program.cs or Module1.vb below the existing using or Imports statements.

    Imports NumberGuessWorkflowActivities
    Imports System.Threading
    
    using NumberGuessWorkflowActivities;
    using System.Threading;
    

    用以下基本 WorkflowInvoker 承载代码替换使用 WorkflowApplication 的代码行。Replace the lines of code that use WorkflowInvoker with the following basic WorkflowApplication hosting code. 此示例承载代码演示承载和调用工作流的基本步骤,但尚不包含用于成功运行本主题中的工作流的功能。This sample hosting code demonstrates the basic steps for hosting and invoking a workflow, but does not yet contain the functionality to successfully run the workflow from this topic. 在以下步骤中,将修改此基本代码并添加其他功能,直到完成应用程序。In the following steps, this basic code is modified and additional features are added until the application is complete.

    备注

    请将以下示例中的 Workflow1 替换为 FlowchartNumberGuessWorkflowSequentialNumberGuessWorkflow和 or StateMachineNumberGuessWorkflow和 depending on which workflow you completed in the previous How to: Create a Workflow 中完成的工作流)。Please replace Workflow1 in these examples with FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow, or StateMachineNumberGuessWorkflow, depending on which workflow you completed in the previous How to: Create a Workflow step. 如果不替换 Workflow1 ,在尝试生成或运行工作流时会出现生成错误。If you do not replace Workflow1 then you will get build errors when you try and build or run the workflow.

    AutoResetEvent syncEvent = new AutoResetEvent(false);
    
    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1());
    
    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来启动工作流,然后等待工作流完成。This code creates a WorkflowApplication, subscribes to three workflow life-cycle events, starts the workflow with a call to Run, and then waits for the workflow to complete. 工作流完成时,将设置 AutoResetEvent ,并且宿主应用程序也完成。When the workflow completes, the AutoResetEvent is set and the host application completes.

设置工作流的输入参数To set input arguments of a workflow

  1. “Program.cs”“Module1.vb” 顶部的现有 usingImports 语句下面,添加以下语句。Add the following statement at the top of Program.cs or Module1.vb below the existing using or Imports statements.

    using System.Collections.Generic;
    
    Imports System.Collections.Generic
    
  2. 将创建新 WorkflowApplication 的代码行替换为以下代码,该代码创建一个参数字典并在创建后将其传递给工作流。Replace the line of code that creates the new WorkflowApplication with the following code that creates and passes a dictionary of parameters to the workflow when it is created.

    备注

    请将以下示例中的 Workflow1 替换为 FlowchartNumberGuessWorkflowSequentialNumberGuessWorkflow和 or StateMachineNumberGuessWorkflow和 depending on which workflow you completed in the previous How to: Create a Workflow 中完成的工作流)。Please replace Workflow1 in these examples with FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow, or StateMachineNumberGuessWorkflow, depending on which workflow you completed in the previous How to: Create a Workflow step. 如果不替换 Workflow1 ,在尝试生成或运行工作流时会出现生成错误。If you do not replace Workflow1 then you will get build errors when you try and build or run the workflow.

    var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };
    
    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1(), inputs);
    
    Dim inputs As New Dictionary(Of String, Object)
    inputs.Add("MaxNumber", 100)
    
    Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
    

    此字典包含一个键为 MaxNumber的元素。This dictionary contains one element with a key of MaxNumber. 输入字典中的键对应工作流根活动的输入参数。Keys in the input dictionary correspond to input arguments on the root activity of the workflow. 工作流使用MaxNumber 来确定随机生成数的上边界。MaxNumber is used by the workflow to determine the upper bound for the randomly generated number.

检索工作流的输出参数To retrieve output arguments of a workflow

  1. 修改 Completed 处理程序以检索并显示工作流使用的轮数。Modify the Completed handler to retrieve and display the number of turns used by the workflow.

    wfApp.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
    

继续执行书签To resume a bookmark

  1. 将以下代码添加到 Main 方法顶部,紧接在现有 AutoResetEvent 声明之后。Add the following code at the top of the Main method just after the existing AutoResetEvent declaration.

    AutoResetEvent idleEvent = new AutoResetEvent(false);
    
    Dim idleEvent As New AutoResetEvent(False)
    
  2. 将以下 Idle 处理程序添加到 Main中,紧接在现有的三个工作流生命周期处理程序的下面。Add the following Idle handler just below the existing three workflow life-cycle handlers in Main.

    wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
    {
        idleEvent.Set();
    };
    
    wfApp.Idle = _
        Sub(e As WorkflowApplicationIdleEventArgs)
            idleEvent.Set()
        End Sub
    

    每次工作流进入空闲状态时,都将调用此处理程序并 idleAction AutoResetEvent 设置。Each time the workflow becomes idle waiting for the next guess, this handler is called and the idleAction AutoResetEvent is set. 下面步骤中的代码使用 idleEventsyncEvent 来确定工作流是在等待下一个猜测还是已完成。The code in the following step uses idleEvent and syncEvent to determine whether the workflow is waiting for the next guess or is complete.

    备注

    在本示例中,宿主应用程序在 CompletedIdle 处理程序中使用自动重置事件将宿主应用程序与工作流的进度同步。In this example, the host application uses auto-reset events in the Completed and Idle handlers to synchronize the host application with the progress of the workflow. 在继续执行书签之前,不需要阻止并等待工作流变为空闲状态,但在此示例中需要同步事件,以使宿主知道工作流是否已完成,或是否在等待使用 Bookmark的更多用户输入。It is not necessary to block and wait for the workflow to become idle before resuming a bookmark, but in this example the synchronization events are required so the host knows whether the workflow is complete or whether it is waiting on more user input by using the Bookmark. 有关详细信息,请参阅书签For more information, see Bookmarks.

  3. 移除对 WaitOne的调用,并替换为收集用户输入并恢复 Bookmark的代码。Remove the call to WaitOne, and replace it with code to gather input from the user and resume the Bookmark.

    移除下面的代码行。Remove the following line of code.

    syncEvent.WaitOne();
    
    syncEvent.WaitOne()
    

    使用下面的示例替换它。Replace it with the following example.

    // 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)
        {
            int Guess;
            if (!Int32.TryParse(Console.ReadLine(), out 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
    

生成并运行应用程序To build and run the application

  1. 解决方案资源管理器 中,右键单击 NumberGuessWorkflowHost 项目,然后选择 “设为启动项目”Right-click NumberGuessWorkflowHost in Solution Explorer and select Set as StartUp Project.

  2. 按 Ctrl+F5 生成并运行应用程序。Press CTRL+F5 to build and run the application. 尝试以尽可能少的次数猜出该数。Try to guess the number in as few turns as possible.

    要尝试其他工作流样式的应用程序,请用 Workflow1WorkflowApplicationFlowchartNumberGuessWorkflow(取决于所需工作流样式)替换代码中用于创建 SequentialNumberGuessWorkflowStateMachineNumberGuessWorkflowTo try the application with one of the other styles of workflow, replace Workflow1 in the code that creates the WorkflowApplication with FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow, or StateMachineNumberGuessWorkflow, depending on which workflow style you desire.

    var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };
    
    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1(), 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 WorkflowFor instructions about how to add persistence to a workflow application, see the next topic, How to: Create and Run a Long Running Workflow.

示例Example

下面的示例是 Main 方法的完整代码清单。The following example is the complete code listing for the Main method.

备注

请将以下示例中的 Workflow1 替换为 FlowchartNumberGuessWorkflowSequentialNumberGuessWorkflow和 or StateMachineNumberGuessWorkflow和 depending on which workflow you completed in the previous How to: Create a Workflow 中完成的工作流)。Please replace Workflow1 in these examples with FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow, or StateMachineNumberGuessWorkflow, depending on which workflow you completed in the previous How to: Create a Workflow step. 如果不替换 Workflow1 ,在尝试生成或运行工作流时会出现生成错误。If you do not replace Workflow1 then you will get build errors when you try and build or run the workflow.

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 WorkflowApplication(new Workflow1(), inputs);

    wfApp.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.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
    {
        Console.WriteLine(e.Reason);
        syncEvent.Set();
    };

    wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
    {
        Console.WriteLine(e.UnhandledException.ToString());
        return UnhandledExceptionAction.Terminate;
    };

    wfApp.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)
        {
            int Guess;
            if (!Int32.TryParse(Console.ReadLine(), out 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

另请参阅See also