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:
- A
User Raises a Indent
- Indent Number is Generated
- User Can Approve or Reject Indent
- If User Approves Flow Continues to Indent Issue
- If the User Rejects the Indent Flow Terminates
- After the Indent is Approved The User can Issue a Partial Issue or Fully Issue
- If the issue is Full then workflow is completed
- 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
Creating
New Indent Order say “Indent 5”
The State is Saved as "NEW"
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” .
In case if the user rejects the order, then workflow will be terminated.
If the User Approves the Order then work flow continuous
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
Using the code
Code details
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 (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 (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;
}
public void OnWorkflowCompleted(WorkflowApplicationCompletedEventArgs e)
{
}
public bool CanApproveToInstance(Guid instanceId)
{
WorkflowApplication instance = this.LoadInstance(instanceId , "AproveorReject");
if (instance.GetBookmarks().Count == 0)
{
return false;
}
else
{
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.