Export (0) Print
Expand All

How to: Create and Run a Long Running Workflow

One of the central features of Windows Workflow Foundation (WF) is the runtime’s ability to persist and unload idle workflows to a database. The steps in How to: Run a Workflow demonstrated the basics of workflow hosting using a console application. Examples were shown of starting workflows, workflow lifecycle handlers, and resuming bookmarks. In order to demonstrate workflow persistence effectively, a more complex workflow host is required that supports starting and resuming multiple workflow instances. This step in the tutorial demonstrates how to create a Windows form host application that supports starting and resuming multiple workflow instances, workflow persistence, and provides a basis for the advanced features such as tracking and versioning that are demonstrated in subsequent tutorial steps.

noteNote:
This tutorial step and the subsequent steps use all three workflow types from How to: Create a Workflow. If you did not complete all three types you can download a completed version of the steps from Windows Workflow Foundation (WF45) - Getting Started Tutorial.

noteNote:
To download a completed version or view a video walkthrough of the tutorial, see Windows Workflow Foundation (WF45) - Getting Started Tutorial.

In this topic

To create the persistence database

  1. Open SQL Server Management Studio and connect to the local server, for example .\SQLEXPRESS. Right-click the Databases node on the local server, and select New Database. Name the new database WF45GettingStartedTutorial, accept all other values, and select OK.

    noteNote:
    Ensure that you have Create Database permission on the local server before creating the database.

  2. Choose Open, File from the File menu. Browse to the following folder: C:\Windows\Microsoft.NET\Framework\4.0.30319\sql\en

    Select the following two files and click Open.

    • SqlWorkflowInstanceStoreLogic.sql

    • SqlWorkflowInstanceStoreSchema.sql

  3. Choose SqlWorkflowInstanceStoreSchema.sql from the Window menu. Ensure that WF45GettingStartedTutorial is selected in the Available Databases drop-down and choose Execute from the Query menu.

  4. Choose SqlWorkflowInstanceStoreLogic.sql from the Window menu. Ensure that WF45GettingStartedTutorial is selected in the Available Databases drop-down and choose Execute from the Query menu.

    Warning Caution:
    It is important to perform the previous two steps in the correct order. If the queries are executed out of order, errors occur and the persistence database is not configured correctly.

To add the reference to the DurableInstancing assemblies

  1. Right-click NumberGuessWorkflowHost in Solution Explorer and select Add Reference.

  2. Select Assemblies from the Add Reference list, and type DurableInstancing into the Search Assemblies box. This filters the assemblies and makes the desired references easier to select.

  3. Check the checkbox beside System.Activities.DurableInstancing and System.Runtime.DurableInstancing from the Search Results list, and click OK.

To create the workflow host form

noteNote:
The steps in this procedure describe how to add and configure the form manually. If desired, you can download the solution files for the tutorial and add the completed form to the project. To download the tutorial files, see Windows Workflow Foundation (WF45) - Getting Started Tutorial. Once the files are downloaded, right-click NumberGuessWorkflowHost and choose Add Reference. Add a reference to System.Windows.Forms and System.Drawing. These references are added automatically if you add a new form from the Add, New Item menu, but must be added manually when importing a form. Once the references are added, right-click NumberGuessWorkflowHost in Solution Explorer and choose Add, Existing Item. Browse to the Form folder in the project files, select WorkflowHostForm.cs (or WorkflowHostForm.vb), and click Add. If you choose to import the form, then you can skip down to the next section, To add the properties and helper methods of the form.

  1. Right-click NumberGuessWorkflowHost in Solution Explorer and choose Add, New Item.

  2. In the Installed templates list, choose Windows Form, type WorkflowHostForm in the Name box, and click Add.

  3. Configure the following properties on the form.

     

    Property Value

    FormBorderStyle

    FixedSingle

    MaximizeBox

    False

    Size

    400, 420

  4. Add the following controls to the form in the order specified and configure the properties as directed.

     

    Control Property Value

    Button

    Name

    NewGame

    Location

    13, 13

    Size

    75, 23

    Text

    New Game

    Label

    Location

    94, 18

    Text

    Guess a number from 1 to

    ComboBox

    Name

    NumberRange

    DropDownStyle

    DropDownList

    Items

    10

    100

    1000

    Location

    228, 12

    Size

    143, 21

    Label

    Location

    13, 43

    Text

    Workflow type

    ComboBox

    Name

    WorkflowType

    DropDownStyle

    DropDownList

    Items

    StateMachineNumberGuessWorkflow

    FlowchartNumberGuessWorkflow

    SequentialNumberGuessWorkflow

    Location

    94, 40

    Size

    277, 21

    Label

    Name

    WorkflowVersion

    Location

    13, 362

    Text

    Workflow version

    GroupBox

    Location

    13, 67

    Size

    358, 287

    Text

    Game

    noteNote:
    The following controls are contained by the GroupBox. When adding them to the form, drop them on the GroupBox.

    Label

    Location

    7, 20

    Text

    Workflow Instance Id

    ComboBox

    Name

    InstanceId

    DropDownStyle

    DropDownList

    Location

    121, 17

    Size

    227, 21

    Label

    Location

    7, 47

    Text

    Guess

    TextBox

    Name

    Guess

    Location

    50, 44

    Size

    65, 20

    Button

    Name

    EnterGuess

    Location

    121, 42

    Size

    75, 23

    Text

    Enter Guess

    Button

    Name

    QuitGame

    Location

    274, 42

    Size

    75, 23

    Text

    Quit

    TextBox

    Name

    WorkflowStatus

    Location

    10, 73

    Multiline

    True

    ReadOnly

    True

    ScrollBars

    Vertical

    Size

    338, 208

  5. Set the AcceptButton property of the form to EnterGuess.

The following example illustrates the completed form.

WF45 Getting Started Tutorial Workflow Host Form

To add the properties and helper methods of the form

The steps in this section add properties and helper methods to the form class that configure the UI of the form to support running and resuming number guess workflows.

  1. Right-click WorkflowHostForm in Solution Explorer and choose View Code.

  2. Add the following using (or Imports) statements at the top of the file with the other using (or Imports) statements.

    using System.Windows.Forms;
    using System.Activities.DurableInstancing;
    using System.Activities;
    using System.Data.SqlClient;
    using System.IO;
    
  3. Add the following member declarations to the WorkflowHostForm class.

    const string connectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI";
    SqlWorkflowInstanceStore store;
    bool WorkflowStarting;
    
    noteNote:
    If your connection string is different, update connectionString to refer to your database.

  4. Add a WorkflowInstanceId property to the WorkflowFormHost class.

    public Guid WorkflowInstanceId
    {
        get
        {
            return InstanceId.SelectedIndex == -1 ? Guid.Empty : (Guid)InstanceId.SelectedItem;
        }
    }
    
    The InstanceId combo box displays a list of persisted workflow instance ids, and the WorkflowInstanceId property returns the currently selected workflow.

  5. Add a handler for the form Load event. To add the handler, switch to Design View for the form, click the Events icon at the top of the Properties window, and double-click Load.

    private void WorkflowHostForm_Load(object sender, EventArgs e)
    {
    
    }
    
  6. Add the following code to WorkflowHostForm_Load.

    // Initialize the store and configure it so that it can be used for
    // multiple WorkflowApplication instances.
    store = new SqlWorkflowInstanceStore(connectionString);
    WorkflowApplication.CreateDefaultInstanceOwner(store, null, WorkflowIdentityFilter.Any);
    
    // Set default ComboBox selections.
    NumberRange.SelectedIndex = 0;
    WorkflowType.SelectedIndex = 0;
    
    ListPersistedWorkflows();
    
    When the form loads, the SqlWorkflowInstanceStore is configured, the range and workflow type combo boxes are set to default values, and the persisted workflow instances are added to the InstanceId combo box.

  7. Add a SelectedIndexChanged handler for InstanceId. To add the handler, switch to Design View for the form, select the InstanceId combo box, click the Events icon at the top of the Properties window, and double-click SelectedIndexChanged.

    private void InstanceId_SelectedIndexChanged(object sender, EventArgs e)
    {
    
    }
    
  8. Add the following code to InstanceId_SelectedIndexChanged. Whenever the user selects a workflow by using the combo box this handler updates the status window.

    if (InstanceId.SelectedIndex == -1)
    {
        return;
    }
    
    // Clear the status window.
    WorkflowStatus.Clear();
    
    // Get the workflow version and display it.
    // If the workflow is just starting then this info will not
    // be available in the persistence store so do not try and retrieve it.
    if (!WorkflowStarting)
    {
        WorkflowApplicationInstance instance =
            WorkflowApplication.GetInstance(this.WorkflowInstanceId, store);
    
        WorkflowVersion.Text =
            WorkflowVersionMap.GetIdentityDescription(instance.DefinitionIdentity);
    
        // Unload the instance.
        instance.Abandon();
    }
    
  9. Add the following ListPersistedWorkflows method to the form class.

    using (SqlConnection localCon = new SqlConnection(connectionString))
    {
        string localCmd =
            "Select [InstanceId] from [System.Activities.DurableInstancing].[Instances] Order By [CreationTime]";
    
        SqlCommand cmd = localCon.CreateCommand();
        cmd.CommandText = localCmd;
        localCon.Open();
        using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
        {
            while (reader.Read())
            {
                // Get the InstanceId of the persisted Workflow
                Guid id = Guid.Parse(reader[0].ToString());
                InstanceId.Items.Add(id);
            }
        }
    }
    
    ListPersistedWorkflows queries the instance store for persisted workflow instances, and adds the instance ids to the cboInstanceId combo box.

  10. Add the following UpdateStatus method and corresponding delegate to the form class. This method updates the status window on the form with the status of the currently running workflow.

    private delegate void UpdateStatusDelegate(string msg);
    public void UpdateStatus(string msg)
    {
        // We may be on a different thread so we need to
        // make this call using BeginInvoke.
        if (InvokeRequired)
        {
            BeginInvoke(new UpdateStatusDelegate(UpdateStatus), msg);
        }
        else
        {
            if (!msg.EndsWith("\r\n"))
            {
                msg += "\r\n";
            }
            WorkflowStatus.AppendText(msg);
    
            WorkflowStatus.SelectionStart = WorkflowStatus.Text.Length;
            WorkflowStatus.ScrollToCaret();
        }
    }
    
  11. Add the following GameOver method and corresponding delegate to the form class. When a workflow completes, this method updates the form UI by removing the instance id of the completed workflow from the InstanceId combo box.

    private delegate void GameOverDelegate();
    private void GameOver()
    {
        if (InvokeRequired)
        {
            BeginInvoke(new GameOverDelegate(GameOver));
        }
        else
        {
            // Remove this instance from the combo box
            InstanceId.Items.Remove(InstanceId.SelectedItem);
            InstanceId.SelectedIndex = -1;
        }
    }
    

To configure the instance store, workflow lifecycle handlers, and extensions

  1. Add a ConfigureWorkflowApplication method to the form class.

    private void ConfigureWorkflowApplication(WorkflowApplication wfApp)
    {    
    }
    
    This method configures the WorkflowApplication, adds the desired extensions, and adds handlers for the workflow lifecycle events.

  2. In ConfigureWorkflowApplication, specify the SqlWorkflowInstanceStore for the WorkflowApplication.

    // Configure the persistence store.
    wfApp.InstanceStore = store;
    
  3. Next, create a StringWriter instance and add it to the Extensions collection of the WorkflowApplication. When a StringWriter is added to the extensions it captures all WriteLine activity output. When the workflow becomes idle, the WriteLine output can be extracted from the StringWriter and displayed on the form.

    // Add a StringWriter to the extensions. This captures the output
    // from the WriteLine activities so we can display it in the form.
    StringWriter sw = new StringWriter();
    wfApp.Extensions.Add(sw);
    
  4. Add the following handler for the Completed event. When a workflow successfully completes, the number of turns taken to guess the number is displayed to the status window. If the workflow terminates, the exception information that caused the termination is displayed. At the end of the handler the GameOver method is called, which removes the completed workflow from the workflow list.

    wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
    {
        if (e.CompletionState == ActivityInstanceState.Faulted)
        {
            UpdateStatus(string.Format("Workflow Terminated. Exception: {0}\r\n{1}",
                e.TerminationException.GetType().FullName,
                e.TerminationException.Message));
        }
        else if (e.CompletionState == ActivityInstanceState.Canceled)
        {
            UpdateStatus("Workflow Canceled.");
        }
        else
        {
            int Turns = Convert.ToInt32(e.Outputs["Turns"]);
            UpdateStatus(string.Format("Congratulations, you guessed the number in {0} turns.", Turns));
        }
        GameOver();
    };
    
  5. Add the following Aborted and OnUnhandledException handlers. The GameOver method is not called from the Aborted handler because when a workflow instance is aborted, it does not terminate, and it is possible to resume the instance at a later time.

    wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
    {
        UpdateStatus(string.Format("Workflow Aborted. Exception: {0}\r\n{1}",
                e.Reason.GetType().FullName,
                e.Reason.Message));
    };
    
    wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
    {
        UpdateStatus(string.Format("Unhandled Exception: {0}\r\n{1}",
                e.UnhandledException.GetType().FullName,
                e.UnhandledException.Message));
        GameOver();
        return UnhandledExceptionAction.Terminate;
    };
    
  6. Add the following PersistableIdle handler. This handler retrieves the StringWriter extension that was added, extracts the output from the WriteLine activities, and displays it in the status window.

    wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
    {
        // Send the current WriteLine outputs to the status window.
        var writers = e.GetInstanceExtensions<StringWriter>();
        foreach (var writer in writers)
        {
            UpdateStatus(writer.ToString());
        }
        return PersistableIdleAction.Unload;
    };
    
    The PersistableIdleAction enumeration has three values: None, Persist, and Unload. Persist causes the workflow to persist but it does not cause the workflow to unload. Unload causes the workflow to persist and be unloaded.

    The following example is the completed ConfigureWorkflowApplication method.

    private void ConfigureWorkflowApplication(WorkflowApplication wfApp)
    {
        // Configure the persistence store.
        wfApp.InstanceStore = store;
    
        // Add a StringWriter to the extensions. This captures the output
        // from the WriteLine activities so we can display it in the form.
        StringWriter sw = new StringWriter();
        wfApp.Extensions.Add(sw);
    
        wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
        {
            if (e.CompletionState == ActivityInstanceState.Faulted)
            {
                UpdateStatus(string.Format("Workflow Terminated. Exception: {0}\r\n{1}",
                    e.TerminationException.GetType().FullName,
                    e.TerminationException.Message));
            }
            else if (e.CompletionState == ActivityInstanceState.Canceled)
            {
                UpdateStatus("Workflow Canceled.");
            }
            else
            {
                int Turns = Convert.ToInt32(e.Outputs["Turns"]);
                UpdateStatus(string.Format("Congratulations, you guessed the number in {0} turns.", Turns));
            }
            GameOver();
        };
    
        wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
        {
            UpdateStatus(string.Format("Workflow Aborted. Exception: {0}\r\n{1}",
                    e.Reason.GetType().FullName,
                    e.Reason.Message));
        };
    
        wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
        {
            UpdateStatus(string.Format("Unhandled Exception: {0}\r\n{1}",
                    e.UnhandledException.GetType().FullName,
                    e.UnhandledException.Message));
            GameOver();
            return UnhandledExceptionAction.Terminate;
        };
    
        wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
        {
            // Send the current WriteLine outputs to the status window.
            var writers = e.GetInstanceExtensions<StringWriter>();
            foreach (var writer in writers)
            {
                UpdateStatus(writer.ToString());
            }
            return PersistableIdleAction.Unload;
        };
    }
    

To enable starting and resuming multiple workflow types

In order to resume a workflow instance, the host has to provide the workflow definition. In this tutorial there are three workflow types, and subsequent tutorial steps introduce multiple versions of these types. WorkflowIdentity provides a way for a host application to associate identifying information with a persisted workflow instance. The steps in this section demonstrate how to create a utility class to assist with mapping the workflow identity from a persisted workflow instance to the corresponding workflow definition. For more information about WorkflowIdentity and versioning, see Using WorkflowApplication Identity and Versioning.

  1. Right-click NumberGuessWorkflowHost in Solution Explorer and choose Add, Class. Type WorkflowVersionMap into the Name box and click Add.

  2. Add the following using or Imports statements at the top of the file with the other using or Imports statements.

    using NumberGuessWorkflowActivities;
    using System.Activities;
    
  3. Replace the WorkflowVersionMap class declaration with the following declaration.

    public static class WorkflowVersionMap
    {
        static Dictionary<WorkflowIdentity, Activity> map;
    
        // Current version identities.
        static public WorkflowIdentity StateMachineNumberGuessIdentity;
        static public WorkflowIdentity FlowchartNumberGuessIdentity;
        static public WorkflowIdentity SequentialNumberGuessIdentity;
    
        static WorkflowVersionMap()
        {
            map = new Dictionary<WorkflowIdentity, Activity>();
    
            // Add the current workflow version identities.
            StateMachineNumberGuessIdentity = new WorkflowIdentity
            {
                Name = "StateMachineNumberGuessWorkflow",
                Version = new Version(1, 0, 0, 0)
            };
    
            FlowchartNumberGuessIdentity = new WorkflowIdentity
            {
                Name = "FlowchartNumberGuessWorkflow",
                Version = new Version(1, 0, 0, 0)
            };
    
            SequentialNumberGuessIdentity = new WorkflowIdentity
            {
                Name = "SequentialNumberGuessWorkflow",
                Version = new Version(1, 0, 0, 0)
            };
    
            map.Add(StateMachineNumberGuessIdentity, new StateMachineNumberGuessWorkflow());
            map.Add(FlowchartNumberGuessIdentity, new FlowchartNumberGuessWorkflow());
            map.Add(SequentialNumberGuessIdentity, new SequentialNumberGuessWorkflow());
        }
    
        public static Activity GetWorkflowDefinition(WorkflowIdentity identity)
        {
            return map[identity];
        }
    
        public static string GetIdentityDescription(WorkflowIdentity identity)
        {        
            return identity.ToString();
       }
    }
    
    WorkflowVersionMap contains three workflow identities that map to the three workflow definitions from this tutorial and is used in the following sections when workflows are started and resumed.

To start a new workflow

  1. Add a Click handler for NewGame. To add the handler, switch to Design View for the form, and double-click NewGame. A NewGame_Click handler is added and the view switches to code view for the form. Whenever the user clicks this button a new workflow is started.

    private void NewGame_Click(object sender, EventArgs e)
    {
    
    }
    
  2. Add the following code to the click handler. This code creates a dictionary of input arguments for the workflow, keyed by argument name. This dictionary has one entry that contains the range of the randomly generated number retrieved from the range combo box.

    var inputs = new Dictionary<string, object>();
    inputs.Add("MaxNumber", Convert.ToInt32(NumberRange.SelectedItem));
    
  3. Next, add the following code that starts the workflow. The WorkflowIdentity and workflow definition corresponding to the type of workflow selected are retrieved using the WorkflowVersionMap helper class. Next, a new WorkflowApplication instance is created using the workflow definition, WorkflowIdentity, and dictionary of input arguments.

    WorkflowIdentity identity = null;
    switch (WorkflowType.SelectedItem.ToString())
    {
        case "SequentialNumberGuessWorkflow":
            identity = WorkflowVersionMap.SequentialNumberGuessIdentity;
            break;
    
        case "StateMachineNumberGuessWorkflow":
            identity = WorkflowVersionMap.StateMachineNumberGuessIdentity;
            break;
    
        case "FlowchartNumberGuessWorkflow":
            identity = WorkflowVersionMap.FlowchartNumberGuessIdentity;
            break;
    };
    
    Activity wf = WorkflowVersionMap.GetWorkflowDefinition(identity);
    
    WorkflowApplication wfApp = new WorkflowApplication(wf, inputs, identity);
    
  4. Next, add the following code which adds the workflow to the workflow list and displays the workflow's version information on the form.

    // Add the workflow to the list and display the version information.
    WorkflowStarting = true;
    InstanceId.SelectedIndex = InstanceId.Items.Add(wfApp.Id);
    WorkflowVersion.Text = identity.ToString();
    WorkflowStarting = false;
    
  5. Call ConfigureWorkflowApplication to configure the instance store, extensions, and workflow lifecycle handlers for this WorkflowApplication instance.

    // Configure the instance store, extensions, and 
    // workflow lifecycle handlers.
    ConfigureWorkflowApplication(wfApp);
    
  6. Finally, call Run.

    'Start the workflow.
    wfApp.Run()
    
    // Start the workflow.
    wfApp.Run();
    
    The following example is the completed NewGame_Click handler.

    private void NewGame_Click(object sender, EventArgs e)
    {
        var inputs = new Dictionary<string, object>();
        inputs.Add("MaxNumber", Convert.ToInt32(NumberRange.SelectedItem));
    
        WorkflowIdentity identity = null;
        switch (WorkflowType.SelectedItem.ToString())
        {
            case "SequentialNumberGuessWorkflow":
                identity = WorkflowVersionMap.SequentialNumberGuessIdentity;
                break;
    
            case "StateMachineNumberGuessWorkflow":
                identity = WorkflowVersionMap.StateMachineNumberGuessIdentity;
                break;
    
            case "FlowchartNumberGuessWorkflow":
                identity = WorkflowVersionMap.FlowchartNumberGuessIdentity;
                break;
        };
    
        Activity wf = WorkflowVersionMap.GetWorkflowDefinition(identity);
    
        WorkflowApplication wfApp = new WorkflowApplication(wf, inputs, identity);
    
        // Add the workflow to the list and display the version information.
        WorkflowStarting = true;
        InstanceId.SelectedIndex = InstanceId.Items.Add(wfApp.Id);
        WorkflowVersion.Text = identity.ToString();
        WorkflowStarting = false;
    
        // Configure the instance store, extensions, and 
        // workflow lifecycle handlers.
        ConfigureWorkflowApplication(wfApp);
    
        // Start the workflow.
        wfApp.Run();
    }
    

To resume a workflow

  1. Add a Click handler for EnterGuess. To add the handler, switch to Design View for the form, and double-click EnterGuess. Whenever the user clicks this button a workflow is resumed.

    private void EnterGuess_Click(object sender, EventArgs e)
    {
    
    }
    
  2. Add the following code to ensure that a workflow is selected in the workflow list, and that the user's guess is valid.

    if (WorkflowInstanceId == Guid.Empty)
    {
        MessageBox.Show("Please select a workflow.");
        return;
    }
    
    int guess;
    if (!Int32.TryParse(Guess.Text, out guess))
    {
        MessageBox.Show("Please enter an integer.");
        Guess.SelectAll();
        Guess.Focus();
        return;
    }
    
  3. Next, retrieve the WorkflowApplicationInstance of the persisted workflow instance. A WorkflowApplicationInstance represents a persisted workflow instance that has not yet been associated with a workflow definition. The DefinitionIdentity of the WorkflowApplicationInstance contains the WorkflowIdentity of the persisted workflow instance. In this tutorial, the WorkflowVersionMap utility class is used to map the WorkflowIdentity to the correct workflow definition. Once the workflow definition is retrieved, a WorkflowApplication is created, using the correct workflow definition.

    WorkflowApplicationInstance instance =
        WorkflowApplication.GetInstance(WorkflowInstanceId, store);
    
    // Use the persisted WorkflowIdentity to retrieve the correct workflow
    // definition from the dictionary.
    Activity wf =
        WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity);
    
    // Associate the WorkflowApplication with the correct definition
    WorkflowApplication wfApp =
        new WorkflowApplication(wf, instance.DefinitionIdentity);
    
  4. Once the WorkflowApplication is created, configure the instance store, workflow lifecycle handlers, and extensions by calling ConfigureWorkflowApplication. These steps must be done every time a new WorkflowApplication is created, and they must be done before the workflow instance is loaded into the WorkflowApplication. After the workflow is loaded, it is resumed with the user's guess.

    // Configure the extensions and lifecycle handlers.
    // Do this before the instance is loaded. Once the instance is
    // loaded it is too late to add extensions.
    ConfigureWorkflowApplication(wfApp);
    
    // Load the workflow.
    wfApp.Load(instance);
    
    // Resume the workflow.
    wfApp.ResumeBookmark("EnterGuess", guess);
    
  5. Finally, clear the guess textbox and prepare the form to accept another guess.

    // Clear the Guess textbox.
    Guess.Clear();
    Guess.Focus();
    
    The following example is the completed EnterGuess_Click handler.

    private void EnterGuess_Click(object sender, EventArgs e)
    {
        if (WorkflowInstanceId == Guid.Empty)
        {
            MessageBox.Show("Please select a workflow.");
            return;
        }
    
        int guess;
        if (!Int32.TryParse(Guess.Text, out guess))
        {
            MessageBox.Show("Please enter an integer.");
            Guess.SelectAll();
            Guess.Focus();
            return;
        }
    
        WorkflowApplicationInstance instance =
            WorkflowApplication.GetInstance(WorkflowInstanceId, store);
    
        // Use the persisted WorkflowIdentity to retrieve the correct workflow
        // definition from the dictionary.
        Activity wf =
            WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity);
    
        // Associate the WorkflowApplication with the correct definition
        WorkflowApplication wfApp =
            new WorkflowApplication(wf, instance.DefinitionIdentity);
    
        // Configure the extensions and lifecycle handlers.
        // Do this before the instance is loaded. Once the instance is
        // loaded it is too late to add extensions.
        ConfigureWorkflowApplication(wfApp);
    
        // Load the workflow.
        wfApp.Load(instance);
    
        // Resume the workflow.
        wfApp.ResumeBookmark("EnterGuess", guess);
    
        // Clear the Guess textbox.
        Guess.Clear();
        Guess.Focus();
    }
    

To terminate a workflow

  1. Add a Click handler for QuitGame. To add the handler, switch to Design View for the form, and double-click QuitGame. Whenever the user clicks this button the currently selected workflow is terminated.

    private void QuitGame_Click(object sender, EventArgs e)
    {
    
    }
    
  2. Add the following code to the QuitGame_Click handler. This code first checks to ensure that a workflow is selected in the workflow list. Then it loads the persisted instance into a WorkflowApplicationInstance, uses the DefinitionIdentity to determine the correct workflow definition, and then initializes the WorkflowApplication. Next the extensions and workflow lifecycle handlers are configured with a call to ConfigureWorkflowApplication. Once the WorkflowApplication is configured, it is loaded, and then Terminate is called.

    if (WorkflowInstanceId == Guid.Empty)
    {
        MessageBox.Show("Please select a workflow.");
        return;
    }
    
    WorkflowApplicationInstance instance =
        WorkflowApplication.GetInstance(WorkflowInstanceId, store);
    
    // Use the persisted WorkflowIdentity to retrieve the correct workflow
    // definition from the dictionary.
    Activity wf = WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity);
    
    // Associate the WorkflowApplication with the correct definition
    WorkflowApplication wfApp =
        new WorkflowApplication(wf, instance.DefinitionIdentity);
    
    // Configure the extensions and lifecycle handlers
    ConfigureWorkflowApplication(wfApp);
    
    // Load the workflow.
    wfApp.Load(instance);
    
    // Terminate the workflow.
    wfApp.Terminate("User resigns.");
    

To build and run the application

  1. Double-click Program.cs (or Module1.vb) in Solution Explorer to display the code.

  2. Add the following using (or Imports) statement at the top of the file with the other using (or Imports) statements.

    using System.Windows.Forms;
    
  3. Remove or comment out the existing workflow hosting code from How to: Run a Workflow, and replace it with the following code.

    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.Run(new WorkflowHostForm());
    }
    
  4. Right-click NumberGuessWorkflowHost in Solution Explorer and choose Properties. In the Application tab, specify Windows Application for the Output type. This step is optional, but if it is not followed the console window is displayed in addition to the form.

  5. Press Ctrl+Shift+B to build the application.

  6. Ensure that NumberGuessWorkflowHost is set as the startup application, and press Ctrl+F5 to start the application.

  7. Select a range for the guessing game and the type of workflow to start, and click New Game. Enter a guess in the Guess box and click Go to submit your guess. Note that the output from the WriteLine activities is displayed on the form.

  8. Start several workflows using different workflow types and number ranges, enter some guesses, and switch between the workflows by selecting from the Workflow Instance Id list.

    Note that when you switch to a new workflow, the previous guesses and progress of the workflow are not displayed in the status window. The reason the status is not available is because it is not captured and saved anywhere. In the next step of the tutorial, How to: Create a Custom Tracking Participant, you create a custom tracking participant that saves this information.




Build Date:

2012-08-21
Show:
© 2014 Microsoft