How to: Run 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.
Note
Each topic in the Getting Started tutorial depends on the previous topics. To complete this topic you must first complete How to: Create an Activity and How to: Create a Workflow.
To create the workflow host project
Open the solution from the previous How to: Create an Activity topic by using Visual Studio 2012.
Right-click the WF45GettingStartedTutorial solution in Solution Explorer and select Add, New Project.
Tip
If the Solution Explorer window is not displayed, select Solution Explorer from the View menu.
In the Installed node, select Visual C#, Workflow (or Visual Basic, Workflow).
Note
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.
Ensure that .NET Framework 4.5 is selected in the .NET Framework version drop-down list. Select Workflow Console Application from the Workflow list. 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.Right-click the newly added NumberGuessWorkflowHost project in Solution Explorer and select Add Reference. Select Solution from the Add Reference list, check the checkbox beside NumberGuessWorkflowActivities, and then click OK.
Right-click Workflow1.xaml in Solution Explorer and choose Delete. Click OK to confirm.
To modify the workflow hosting code
Double-click Program.cs or Module1.vb in Solution Explorer to display the code.
Tip
If the Solution Explorer window is not displayed, select Solution Explorer from the View menu.
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);
This generated hosting code uses 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 provides a richer model for executing workflows that includes notification of life-cycle events, execution control, bookmark resumption, and persistence. This example uses bookmarks and WorkflowApplication is used for hosting the workflow. 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;
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.
Note
You must replace
Workflow1
in these examples withFlowchartNumberGuessWorkflow
,SequentialNumberGuessWorkflow
, orStateMachineNumberGuessWorkflow
, depending on which workflow you completed in the previous How to: Create a Workflow step. If you do not replaceWorkflow1
then you will get build errors when you try and build or run the workflow.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()
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. When the workflow completes, the AutoResetEvent is set and the host application completes.
To set input arguments of a workflow
Add the following statement at the top of Program.cs or Module1.vb below the existing
using
orImports
statements.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.
Note
Please replace
Workflow1
in these examples withFlowchartNumberGuessWorkflow
,SequentialNumberGuessWorkflow
, orStateMachineNumberGuessWorkflow
, depending on which workflow you completed in the previous How to: Create a Workflow step. If you do not replaceWorkflow1
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(_wf, inputs) {
Dim inputs As New Dictionary(Of String, Object) inputs.Add("MaxNumber", 100) Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
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
is used by the workflow to determine the upper bound for the randomly generated number.
To retrieve output arguments of a workflow
Modify the Completed handler to retrieve and display the number of turns used by the workflow.
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
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)
Add the following Idle handler just below the existing three workflow life-cycle handlers in
Main
.Idle = delegate (WorkflowApplicationIdleEventArgs e) { idleEvent.Set(); } };
wfApp.Idle = Sub(e As WorkflowApplicationIdleEventArgs) idleEvent.Set() End Sub
Each time the workflow becomes idle waiting for the next guess, this handler is called and the
idleAction
AutoResetEvent is set. The code in the following step usesidleEvent
andsyncEvent
to determine whether the workflow is waiting for the next guess or is complete.Note
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. 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.
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) { 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
To build and run the application
Right-click NumberGuessWorkflowHost in Solution Explorer and select Set as StartUp Project.
Press CTRL+F5 to build and run the application. Try to guess the number in as few turns as possible.
To try the application with one of the other styles of workflow, replace
Workflow1
in the code that creates the WorkflowApplication withFlowchartNumberGuessWorkflow
,SequentialNumberGuessWorkflow
, orStateMachineNumberGuessWorkflow
, depending on which workflow style you desire.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)
For instructions about how to add persistence to a workflow application, see the next topic, How to: Create and Run a Long Running Workflow.
Example
The following example is the complete code listing for the Main
method.
Note
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. 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(_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