Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Indent Process using State Machine Workflow 4.0 with SQL Server persistence

4.75/5 (3 votes)
6 Sep 2013CPOL3 min read 32.6K   1.3K  
Indent Process using State Machine Workflow 4.0 with SQL Server persistence.

Introduction   

This is a sample workflow application developed using Visual studio 2010, using State Machine with SQL Server Persistence. State Machine in .NET 3.5 is different from state machine 4.0, the SQL Server Database Schema and Tables have changed from previous version. The State Machine Workflows are based on state, transition and Final state. There are can be multiple Final States and as Flow continuous the state transition takes place. One more reason is that the workflow direction can be changed based on Human decision.

Background

In order to create the Application, you must have following tools/packages installed in your system.

You must have the following items:

  • Microsoft Visual Studio 2010 Service Pack 1
  • Microsoft .NET Framework 4 Platform Update 1 - Runtime Update (KB2478063)
  • Microsoft .NET Framework 4 Platform Update 1 – Design-time Update for Visual Studio 2010 SP1 (KB2495593)      
  • SQL Server with a Persistence Database as Instance Store as “PersistenceDataBase”

Process to set up Persistence Database:

Microsoft is providing Database Scripts to set up instance store, the scripts can be found in the path: C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en.

The Indent Process

Indent Process WorkFlow Steps:

  1. A User Raises a Indent
  2. Indent Number is Generated
  3. User Can Approve or Reject Indent
  4. If User Approves Flow Continues to Indent Issue
  5. If the User Rejects the Indent Flow Terminates
  6. After the Indent is Approved The User can Issue a Partial Issue or Fully Issue
  7. If the issue is Full then workflow is completed
  8. If the issue is Partial The Flow continuous till fully issue 

 The State of the WorkFlow: 

  •   New
  •   Approve or Reject
  •   Partial
  •   Full 

These are Bookmarks in the Work Flow. We have already discussed that State machine Workflows are driven by user decision and empower this Bookmarks are used. The Bookmark hold the control of work flow and it is again resumed after human intervention. In this Example there are two scenarios where

 Activity

Image 1 

 Creating New  Indent Order say “Indent 5”

Image 2

Image 3

The State is Saved as "NEW"     

Image 4 

For every indent order a new work flow instance is created, this workflow instance is persisted in database. 

The Workflow GUID is saved in instance store database, Bookmarks are displayed and current execution status of the Work flow status is displayed, here in this query output it is showing as “idle” .

Image 5

Image 6

In case if the user rejects the order, then workflow will be terminated.

If the User Approves the Order then work flow continuous 

Image 7

Image 8

Now after Approving the Work Flow , the Stock needs to be issued . The second intervention of the user is required to continue the workflow. The user needs to issue stock. If the user issue the stock then workflow continuous however according to State machine flow diagram if the issue is partial it will not get completed. If the user issues full stock then only the work is completed 

Image 9

Using the code

Code details 

C#
using System;
using System.Activities;
using System.Activities.DurableInstancing;
using System.Collections.Generic;
using System.Runtime.DurableInstancing;
using System.Threading;
using System.Xml.Linq;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Activities.Hosting;
using System.Activities.Tracking;
using System.IO;
namespace IndentingProcess
{
   public class IndentOrderHost : IIndentOrderHost
    {
         IDictionary<Guid, WorkflowApplication> instances;
        static SqlWorkflowInstanceStore sqlWorkflowInstanceStore = SetupSqlpersistenceStore();
        static InstanceStore instanceStore;
        static AutoResetEvent instanceUnloaded = new AutoResetEvent(false);
        
        static Guid id;
        public IndentOrderHost()
        {
            instances = new Dictionary<Guid, WorkflowApplication>();
        }
        
        public WorkflowApplication LoadInstance(Guid instanceId)
        {
            // if the instance is in memory, return it
            if (instances.ContainsKey(instanceId))
                return this.instances[instanceId];
            Activity wf = new Activity2();
            IDictionary<string, object> inputs = new Dictionary<string, object>();
            inputs.Add("OrderStatus","Approve");
            WorkflowApplication instance = new WorkflowApplication(wf, inputs);
            instance.InstanceStore = sqlWorkflowInstanceStore;
            instance.Completed += OnWorkflowCompleted;
            instance.Idle += OnIdle;
            instance.Load(instanceId);
            instances.Add(instanceId, instance);
            return instance;
        }

        public System.Activities.WorkflowApplication CreateAndRun(string OrderName)
        {
            IDictionary<string, object> inputs = new Dictionary<string, object>();
            inputs.Add("OrderStatus", "New");


            Activity wf = new Activity2();
            WorkflowApplication instance = new WorkflowApplication(wf, inputs);
            instance.InstanceStore = sqlWorkflowInstanceStore;
            instance.PersistableIdle += OnIdleAndPersistable;
            instance.Completed += OnWorkflowCompleted;
            instance.Idle += OnIdle;
            string strGUID = "";
            strGUID = instance.Id.ToString();
            SaveRecords(strGUID, OrderName);
            instance.Persist();
            this.instances.Add(instance.Id, instance);
            instance.Run();

            return instance;
        }
        public WorkflowApplication LoadInstance(Guid instanceId , string status)
        {
            // if the instance is in memory, return it
            if (instances.ContainsKey(instanceId))
                return instances[instanceId];
            IDictionary<string, object> inputs = new Dictionary<string, object>();
            inputs.Add("OrderStatus", "New");


            Activity wf = new Activity2();
            WorkflowApplication instance = new WorkflowApplication(wf);
            
            instance.InstanceStore = sqlWorkflowInstanceStore;
            instance.Completed += OnWorkflowCompleted;
            instance.Idle += OnIdle;
            instance.Load(instanceId);
            instances.Add(instanceId, instance);
            return instance;
        }


       
        public void OnIdle(WorkflowApplicationIdleEventArgs e)
        {
        }

        public PersistableIdleAction OnIdleAndPersistable(WorkflowApplicationIdleEventArgs e)
        {
            return PersistableIdleAction.Persist;
        }

        // executed when instance is persisted
        public void OnWorkflowCompleted(WorkflowApplicationCompletedEventArgs e)
        {
            
        }

        public bool CanApproveToInstance(Guid instanceId)
        {
            WorkflowApplication instance = this.LoadInstance(instanceId , "AproveorReject");

            // if there are no bookmarks, the process has finalized
            if (instance.GetBookmarks().Count == 0)
            {
                
                return false;
            }
            else // if there are bookmarks, check if one of them
            // correspond with the "logged" vendor
            {
                foreach (BookmarkInfo bookmarkInfo in instance.GetBookmarks())
                {
                    if (bookmarkInfo.BookmarkName.Equals("New"))
                    {
                        
                        return true;
                    }
                }
                return false;
            }
        }

        public void UpdateStatus(Guid instanceId, int OrderID, string Status)
        {
            WorkflowApplication instance = this.LoadInstance(instanceId, Status);
            string bookmarkName = "New";
            if (Status == "Full" )
            {
                 bookmarkName = "Approve";
                instance.ResumeBookmark(bookmarkName, Status);
            }
            else
            { instance.ResumeBookmark(bookmarkName, Status); }
            
            updaterecords(Status, OrderID);
            instance.Completed = (CompletedArgs) =>
            {
                if (CompletedArgs.CompletionState == ActivityInstanceState.Closed)
                {

                }
                else
                { instance.Persist(); }

            };
        }


        private static SqlWorkflowInstanceStore SetupSqlpersistenceStore()
        {
            try
            {
                SqlWorkflowInstanceStore sqlWFInstanceStore =
                 new SqlWorkflowInstanceStore("Data Source=Server;Initial " + 
                 "Catalog=PersistenceDatabase;uid=pocuser;password=pocuser;");
                InstanceHandle handle = sqlWFInstanceStore.CreateInstanceHandle();
                InstanceView view = sqlWFInstanceStore.Execute(handle, 
                        new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(5));
                handle.Free();
                sqlWFInstanceStore.DefaultInstanceOwner = view.InstanceOwner;
                return sqlWFInstanceStore;
            }
            catch (Exception ex)
            {
                string strex = ex.Message;
                return null;
            }
          
        }
        private void SaveRecords(string strGUID, string strOrderName  )
        {
            SqlConnection sqlCon = new SqlConnection("Data Source=Server;" + 
              "Initial Catalog=PersistenceDatabase;uid=pocuser;password=pocuser;Asynchronous Processing=True");
            SqlCommand sqlCmd = new SqlCommand();
            sqlCmd.CommandText = "INSERT INTO [ContosoHR].[dbo].[IndentOrder]" + 
              " ([ID],[OrderStatus],[OrderName]) VALUES ('" + strGUID + "', 'New','" + 
              strOrderName + "') ";
            sqlCmd.Connection = sqlCon;
            sqlCon.Open();
            int intResult = sqlCmd.ExecuteNonQuery();
            sqlCon.Close();
        }
        private void updaterecords(string status ,int strGUID )
        {
            SqlConnection sqlCon = new SqlConnection("Data Source=Server;Initial " + 
              "Catalog=PersistenceDatabase;uid=pocuser;password=pocuser;Asynchronous Processing=True");
            SqlCommand sqlCmd = new SqlCommand();
            sqlCmd.CommandText = "update  [ContosoHR].[dbo].[IndentOrder]  set [OrderStatus]  = '" + 
              status + "' where Orderid  = " + strGUID + " ";
            sqlCmd.Connection = sqlCon;
            sqlCon.Open();
            int intResult = sqlCmd.ExecuteNonQuery();
            sqlCon.Close();
        }
    }
}

Conclusion 

Using state machine and workflow engine developers can create a process to regulate activities.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)