Click here to Skip to main content
Click here to Skip to main content

Windows Workflow Foundation (WF) and DotNetNuke™

, 14 Sep 2008
Rate this:
Please Sign up or sign in to vote.
This article describes an implementation using a DotNetNuke module communicating with a Windows Workflow service hosted in a Web Service.

shotofapp.jpg

Introduction

Windows Workflow Foundation is a framework for managing workflows. This article describes an implementation using a DotNetNuke module communicating with a Windows Workflow service hosted in a Web Service.

Windows Workflow Foundation is a powerful enterprise level framework that allows you to model business processes and execute and manage workflows. Yes, you could achieve the same functionally purely in procedural code; however, for complex business processes, you will usually end up with an unmanageable heap of spaghetti code. Windows Workflow Foundation allows you to graphically model the workflow processes and easily change them.

The example application is called "Vacation Request". The application allows a user to start a vacation request and have it approved (or not). The application is not really practical, it is simply an example of the minimum required components needed to create an application using a DotNetNuke module communicating with a Windows Workflow service (hosted as a web service).

The following is not a tutorial. It is just an overview of the steps used to create the sample code. It does not contain important components you would need to add in a real workflow application such as the SqlWorkflowPersistenceService. It also does not contain security for the web methods. See Implementing "Super Tight Security" for an example of web method security.

Creating the Web Service

ASP.NET 3.51 SP1 (or higher) includes additional components to host Windows Workflow using WCF. This example, however, uses normal .asmx web services.

Visual Studio 2008 is used to create a new project called VacationRequest.

A interface class (IVacationRequest) is created that indicates the web methods that will be used:

using System;

namespace VacationRequest
{
    public interface IVacationRequest
    {
        /// Activate the workflow
        void StartWorkflow();

        /// Perform a division operation
        bool RequestedDays(int RequestedDays);

        /// Retrieve the last divide result
        String VacationRequestStatus();

        /// Allow the workflow to stop
        void StopWorkflow();
    }
}

A Sequential Workflow (VacationRequestWorkflow) class is added to the project.

Activities for the class (to implement the flow) are assembled using the designer in Visual Studio.

A WhileActivity (ProcessRequest) is used to keep the workflow instance active while the EventDrivenActivitys are repeatedly called (note, that this example does not use persistence services so the workflow instance will eventually terminate when the ASP.NET process terminates).

ProcessRequest will normally terminate only when the CheckIsTimeToStop variable (in the code-behind) is true. Notice that CheckIsTimeToStop is set as the Condition.

The ProcessRequest activity uses three EventDrivenActivitys to group the RequestedDays, VacationRequestStatus, and StopWorkflow web methods (the StartWorkflow method is mapped to the StartVacationRequest activity (at the top of the workflow diagram)).

For example, EventDrivenActivity1 is used to group a WebServiceInputActivity (RequestedDaysInput), a CodeActivity (ApproveRequest), and a WebServiceOutputActivity (RequestedDaysOutput).

The WebServiceInputActivity (RequestedDaysInput) is used to map the RequestedDays Web Service method, and the parameters passed to it, to the workflow.

The CodeActivity (ApproveRequest) executes code (ApproveRequest_ExecuteCode) that will change the status of the Vacation Request (the RequestStatus variable in the code behind).

The WebServiceOutputActivity (RequestedDaysOutput) completes the Web Service call. This activity could return values from the workflow. In this example, it simply returns a positive value.

The following code is the complete code-behind for the class:

using System;
using System.Workflow.Activities;

namespace VacationRequest
{
    public sealed partial class VacationRequestWorkflow : 
                  SequentialWorkflowActivity
    {
        public int RequestDays;
        public string RequestStatus;
        public Boolean isTimeToStop = false;

        public VacationRequestWorkflow()
        {
            InitializeComponent();
        }

        // This method will return the value of isTimeToStop
        // The value of isTimeToStop will be set by other
        // methods in the workflow
        private void CheckIsTimeToStop(object sender, ConditionalEventArgs e)
        {
            e.Result = !(isTimeToStop);
        }

        // Request will be approved if RequestDays is less than 3 days
        private void ApproveRequest_ExecuteCode(object sender, EventArgs e)
        {
            RequestStatus = (RequestDays < 3) ? ''Approved'' : ''Not Approved'';}

        // When this method is called isTimeToStop will be set to true
        // This will cause the workflow to terminate
        private void webServiceInputActivity1_InputReceived(object sender, EventArgs e)
        {
            //Stop the WhileActivity
            isTimeToStop = true;
        }
    }
}

The code is complete. Publish as Web Service is now selected.

A web service project is created, and when the VacationRequest.VacationRequestWorkflow_WebService.asmx page is viewed in the web browser...

the web methods are displayed.

Note: If the sample code for the Web Service is run using IIS7, the Application Pool must be set to "Classic .NET AppPool".

The DotNetNuke Module Web Service Reference

A DotNetNuke module is then created that will be able to communicate with the Windows Workflow Foundation Web Service. First, a web proxy is created.

The DotNetNuke website is opened in Visual Studio and a new project is added.

A simple class project called VacationWebService is created.

A web reference is created that points to the Web Service created in the earlier step. Note, we will be able to change the address to the Web Service programmatically in a later step.

The DotNetNuke Module

The DotNetNuke module allows you to enter the URL to the Web Service, and after clicking the Create Vacation Request button, retrieve the WorkflowID. The WorkflowID will be passed in all subsequent requests for the current instance of the workflow. The Web Service is expecting the WorkflowID to be passed in the header.

The following is the code for the button that performs the task:

protected void btnCreateVacationRequest_Click(object sender, EventArgs e)
{
    // Reference to the web service
    VacationRequestWorkflow_WebService VacationRequest = 
                    new VacationRequestWorkflow_WebService();
    // Enable cookies
    VacationRequest.CookieContainer = new System.Net.CookieContainer();
    // Set the address to the web service
    VacationRequest.Url = txtWebserviceURL.Text.Trim();
    // Call the method to start the workflow
    VacationRequest.StartWorkflow();
    // Create a URI
    Uri VacationRequestUri = new Uri(VacationRequest.Url);
    // The web service will pass
    // the WorkflowInstanceId back in the cookie collection 
    // Use the URI to obtain a collection of the cookies
    CookieCollection mycollection = 
       VacationRequest.CookieContainer.GetCookies(VacationRequestUri);
    // Loop through each cookie until the WF_WorkflowInstanceId cookie is found
    foreach (Cookie Cookie in mycollection)
    {
        if (Cookie.Name == ''WF_WorkflowInstanceId'')
        {
            // Display the WF_WorkflowInstanceId  value in the text box
            txtCurrentWorkflowID.Text = Cookie.Value;
        }
    }
}

The next step is to request days.

The following is the code used to perform this task:

protected void btnRequestDays_Click(object sender, EventArgs e)
{
    // Reference to the web service
    VacationRequestWorkflow_WebService VacationRequest = 
                     new VacationRequestWorkflow_WebService();
    // Set the address to the web service
    VacationRequest.Url = txtWebserviceURL.Text.Trim();
    // Create a URI
    Uri VacationRequestUri = new Uri(VacationRequest.Url);
    // Enable cookies
    VacationRequest.CookieContainer = new System.Net.CookieContainer();
    // Use the URI to obtain a collection of the cookies
    CookieCollection mycollection = 
       VacationRequest.CookieContainer.GetCookies(VacationRequestUri);
    // Add the current WorkflowInstanceId to the cookie collection 
    // that will be passed to the web service
    VacationRequest.CookieContainer.SetCookies
        (
        VacationRequestUri,
        String.Format(''{0}={1}'', ''WF_WorkflowInstanceId'', 
                      txtCurrentWorkflowID.Text.Trim())
        );
    // Call the RequestedDays web method and pass the requested days
    VacationRequest.RequestedDays(Convert.ToInt32(txtRequestedDays.Text));
}

Now the status of the Vacation Request can be retrieved. If the request is not approved, you can change the days and resubmit using the same workflow instance.

The following is the code used to perform this task:

protected void btnGetStatus_Click(object sender, EventArgs e)
{
    // Reference to the web service
    VacationRequestWorkflow_WebService VacationRequest = 
                 new VacationRequestWorkflow_WebService();
    // Set the address to the web service
    VacationRequest.Url = txtWebserviceURL.Text.Trim();
    // Create a URI
    Uri VacationRequestUri = new Uri(VacationRequest.Url);
    // Enable cookies
    VacationRequest.CookieContainer = new System.Net.CookieContainer();
    // Use the URI to obtain a collection of the cookies
    CookieCollection mycollection = 
            VacationRequest.CookieContainer.GetCookies(VacationRequestUri);
    // Add the current WorkflowInstanceId to the cookie collection 
    // that will be passed to the web service
    VacationRequest.CookieContainer.SetCookies
        (
        VacationRequestUri,
        String.Format(''{0}={1}'', ''WF_WorkflowInstanceId'', 
                      txtCurrentWorkflowID.Text.Trim())
        );
    // Call the VacationRequestStatus web method and retrieve the status
    lblRequestStatus.Text = VacationRequest.VacationRequestStatus();
}

The final step is to stop the workflow.

The following is the code used to perform this task:

protected void btnStopWorkflow_Click(object sender, EventArgs e)
{
    // Reference to the web service
    VacationRequestWorkflow_WebService VacationRequest = 
              new VacationRequestWorkflow_WebService();
    // Set the address to the web service
    VacationRequest.Url = txtWebserviceURL.Text.Trim();
    // Create a URI
    Uri VacationRequestUri = new Uri(VacationRequest.Url);
    // Enable cookies
    VacationRequest.CookieContainer = new System.Net.CookieContainer();
    // Use the URI to obtain a collection of the cookies
    CookieCollection mycollection = 
       VacationRequest.CookieContainer.GetCookies(VacationRequestUri);
    // Add the current WorkflowInstanceId to the cookie collection 
    // that will be passed to the web service
    VacationRequest.CookieContainer.SetCookies
        (
        VacationRequestUri,
        String.Format(''{0}={1}'', ''WF_WorkflowInstanceId'', 
                      txtCurrentWorkflowID.Text.Trim()));
    // Stop the workflow and delete the workflow instance
    VacationRequest.StopWorkflow();
}

If you try to request status of the WorkflowInstanceID after the workflow has been stopped and deleted, you will get an error because the workflow instance no longer exists.

A Simple Example with Big Possibilities

The real power of Windows Workflow Foundation is its ability to allow you to easily change a workflow. A workflow can send emails, communicate with legacy systems, and log all events. The workflow can also have events that will send an email if too much time has passed before a request is approved.

In addition, client technologies such as Silverlight can be used to provide a deeper richer interface to perform tasks.

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

defwebserver
Software Developer (Senior) http://ADefWebserver.com
United States United States
Michael Washington is a Microsoft MVP. He is a ASP.NET and C# programmer.
 
He is the founder of http://LightSwitchHelpWebsite.com
 
He has a son, Zachary and resides in Los Angeles with his wife Valerie.
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmvpKanasz Robert5-Nov-12 0:45 
General'' in the source code is suppose to be a single double quote Pinmemberdefwebserver15-Sep-08 3:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 14 Sep 2008
Article Copyright 2008 by defwebserver
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid