Click here to Skip to main content
15,895,084 members
Articles / Programming Languages / C# 4.0

Indent Process using State Machine Workflow 4.0 with SQL Server persistence

Rate me:
Please Sign up or sign in to vote.
4.75/5 (3 votes)
6 Sep 2013CPOL3 min read 32.2K   1.3K   7   8
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)


Written By
Team Leader
India India
I am Siva having 8 years of experience in Microsoft Technologies, my passion is coding and i like to enhance my skills , my hobbies are reading books, in free time i would like to spend time with my wife(Srividya) and my kid(Dwani)

Comments and Discussions

 
BugFacing the instance lock while calling from web application Pin
romiltonfdo26-Jan-16 19:49
romiltonfdo26-Jan-16 19:49 
QuestionInstance locked exception Pin
Member 983230330-Dec-13 2:58
Member 983230330-Dec-13 2:58 
AnswerRe: Instance locked exception Pin
Siva Shanker Kadasi30-Dec-13 3:31
professionalSiva Shanker Kadasi30-Dec-13 3:31 
when we create an instance of The workflow , it is loaded in Memory , it persist in memory . if we try to again create a new instance it this will throw as Locked by a different instance owner . this is because the prevoius instance and newly created instance have same GUID ( the workflow id). in such scenarios either unload the workflow and recreate the workflow or else check if the instance exist and use same object
QuestionWhat will Happen if the workflow process is killed in middle Pin
ElinaRajen9-Oct-13 0:33
ElinaRajen9-Oct-13 0:33 
AnswerRe: What will Happen if the workflow process is killed in middle Pin
Siva Shanker Kadasi9-Oct-13 2:41
professionalSiva Shanker Kadasi9-Oct-13 2:41 
QuestionQuestion Pin
ramprasad addanki12-Sep-13 0:58
ramprasad addanki12-Sep-13 0:58 
QuestionQuestion Pin
ramprasad addanki12-Sep-13 0:55
ramprasad addanki12-Sep-13 0:55 
AnswerRe: Question Pin
Siva Shanker Kadasi12-Sep-13 20:01
professionalSiva Shanker Kadasi12-Sep-13 20:01 

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

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