Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / XHTML

Windows Workflow Foundation (WF) and DotNetNuke™

4.88/5 (6 votes)
14 Sep 2008BSD4 min read 1   1.2K  
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.

Image 2

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:

C#
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();
    }
}

Image 3

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

Image 4

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

Image 5

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

Image 6

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

Image 7

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

Image 8

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

Image 9

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

Image 10

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:

C#
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;
        }
    }
}

Image 11

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

Image 12

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

Image 13

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.

Image 14

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

Image 15

A simple class project called VacationWebService is created.

Image 16

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

Image 17

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:

C#
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;
        }
    }
}

Image 18

The next step is to request days.

The following is the code used to perform this task:

C#
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));
}

Image 19

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:

C#
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();
}

Image 20

The final step is to stop the workflow.

The following is the code used to perform this task:

C#
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();
}

Image 21

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