Basic Sequential Workflows

Summary

Introduction

TO DO

Example

This example performs a very simple logic: it reads user-specific flags from a configuration file to determine whether the user is required to log-in (ieaLogin activity) and what privileges the user should have(ieaPrivileges). The overall workflow is called Workflow1 and is shown below:

The following explains the workflow structure:

The first activity is ieaLogin activity of type System.Workflow.Activities.IfElseActivity. IfElseActivity activity is similar to an If-else-if statement; it conditionally runs two or more activities (iebLogin and iebNoLogin) of type IfElseBranchActivity.

Each branch of type IfElseBranchActivity must have a condition to determine whether the branch should execute or note. The final branch is not required to have a condition, in which case it always evaluates to true. The following shows the Properties window for the first IfElseBranchActivity activity. Note how the Condition property is set to IsLoginRequired:

   
 

private void IsLoginRequired(object sender, ConditionalEventArgs e)
{
    Trace.WriteLine("IsLoginRequired");
    e.Result = _bLogin;                 // if e.Result is true, iebLogin branch will execute.
}

iegLogin branch will execute, but what will it execute? In this case, a CodeActivity activity is configured via its properties to execute the Login method:

   

private void Login(object sender, EventArgs e)
{
    Trace.WriteLine("Login");

    // Perform login operations here
    ...
}

Note that code executed by a CodeActivity activity is executed in a synchronous manner. CodeActivity does not yield its thread until the thread is finished. Therefore, the code is expected to be performed and should not block on some external resource. Typical use of CodeActivity is to examine workflow instance state and change local variables and messages.

The same exact approach is used for the ieaPrivileges activity and its branches (iebGuest, iebUser, iebAdmin).

The following code extracted from the designer-generated code shows the actual types used for each workflow item:

private IfElseActivity       ieaLogin;
private IfElseBranchActivity iefNoLogin;
private IfElseBranchActivity iebLogin;
private CodeActivity         caDoNotLogin;
private CodeActivity         caLogin;

private IfElseActivity       ieaPrivileges;
private IfElseBranchActivity iebAdmin;
private IfElseBranchActivity iebUser;
private IfElseBranchActivity iebGuest;
private CodeActivity         caAdmin;
private CodeActivity         caUser;
private CodeActivity         caGuest;

Full-code, except for the designer-generated code, is shown below:

// Code for work flow Workflow1. Designer-generated code is not included
public sealed partial class Workflow1: SequentialWorkflowActivity
{
    #region Data members
    private bool _bLogin = false;
    private string _Privilege = String.Empty;
    #endregion

    public Workflow1()
    {
        InitializeComponent();    // Invokes designer-generated code (not shown)
        InitFlags();
    }

    #region Helpers
    private void InitFlags()
    {
        // Initialize flags. These flags are used to determine which activities are executed
        _bLogin = Convert.ToBoolean(ConfigurationManager.AppSettings["Login"]);
        _Privilege = Convert.ToString(ConfigurationManager.AppSettings["Privilege"]);
    }
    #endregion

    /// This region of code uses an ifElseActivity to determine if a user need to log
    /// in or not. Note that for the iebLogin activity, the Condition property should
    /// be set on the left-most branch (iebLogin). If you set if on any other branch,
    /// you will get the following error:
    /// error WF278: Activity 'iebLogin' validation failed: Property 'Condition' is not set

    #region ieaLogin
    // Determine if user needs to log in
    private void IsLoginRequired(object sender, ConditionalEventArgs e)
    {
        Trace.WriteLine("IsLoginRequired");
        e.Result = _bLogin;                 // User should log in
    }

    private void Login(object sender, EventArgs e)
    {
        Trace.WriteLine("Login");

        // Perform login operations
    }

    private void DoNotLogin(object sender, EventArgs e)
    {
        Trace.WriteLine("DoNotLogin");

        // Set flag to indicate user not required to login

    }
    #endregion

    /// This region of code uses an ifElseActivity to determine user permissions.
    /// Note that there are three branches and the Condition property needs to be
    /// set on each branch (last branch can leave its Condition property not set
    /// and hence acts as the 'else' of an 'if' statement).
    /// Just like an if-elseif-else statement, runtime starts by evaluating the
    /// ieaPrivileges activity from left-to-right starting with IsGuest, then
    /// IsUser, and finally IsAdmin (look at output traces)

    #region ieaPrivileges
    private void IsGuest(object sender, ConditionalEventArgs e)
    {
        Trace.WriteLine("IsGuest");

        // Is logged under a guest
        e.Result = (_Privilege == "guest") ? true : false;
    }

    private void IsUser(object sender, ConditionalEventArgs e)
    {
        Trace.WriteLine("IsUser");

        // Is logged under a user
        e.Result = (_Privilege == "user") ? true : false;
    }

    private void caGuest_ExecuteCode(object sender, EventArgs e)
    {
        Trace.WriteLine("caGuest_ExecuteCode");
    }

    private void caUser_ExecuteCode(object sender, EventArgs e)
    {
        Trace.WriteLine("caUser_ExecuteCode");
    }

    private void caAdmin_ExecuteCode(object sender, EventArgs e)
    {
        Trace.WriteLine("caAdmin_ExecuteCode");
    }
    #endregion
}

// Main program code to initialize and start the workflow
class Program
{
    static void Main(string[] args)
    {
        using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
        {
            // Setup evnet to capture workflow events
            AutoResetEvent waitHandle = new AutoResetEvent(false);
            workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
            {
                waitHandle.Set();
            };

            workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
            {
                Console.WriteLine(e.Exception.Message);
                waitHandle.Set();
            };

            // Create and start the workflow. BasicSequentialWorkflow.Workflow1 is the actual workflow implementation
            WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(BasicSequentialWorkflow.Workflow1));
            instance.Start();

            waitHandle.WaitOne();
        }
    }
}