使用状态机工作流 4.0 和 SQL Server 持久性的申购流程






4.75/5 (3投票s)
使用状态机工作流 4.0 和 SQL Server 持久性的申购流程。
引言
这是一个使用 Visual Studio 2010 开发的示例工作流应用程序,它使用状态机和 SQL Server 持久性。.NET 3.5 中的状态机与 4.0 不同,SQL Server 数据库模式和表已从以前的版本更改。状态机工作流基于状态、转换和最终状态。可以有多个最终状态,并且随着流程的继续,状态转换发生。另一个原因是工作流方向可以基于人为决策而改变。
背景
为了创建应用程序,您必须在系统中安装以下工具/包。
您必须具备以下项目
- Microsoft Visual Studio 2010 Service Pack 1
- Microsoft .NET Framework 4 Platform Update 1 - Runtime Update (KB2478063)
- Microsoft .NET Framework 4 Platform Update 1 – Visual Studio 2010 SP1 的设计时更新 (KB2495593)
- 具有持久性数据库作为实例存储的 SQL Server 作为“PersistenceDataBase”
设置持久性数据库的过程
Microsoft 提供了数据库脚本来设置实例存储,这些脚本可以在以下路径中找到:C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en。
申购流程
申购流程工作流步骤
- 用户提出申购
- 生成申购单号
- 用户可以批准或拒绝申购
- 如果用户批准,流程继续到申购发放
- 如果用户拒绝,申购流程终止
- 申购被批准后,用户可以部分发放或完全发放
- 如果发放是完全发放,则工作流完成
- 如果发放是部分发放,则流程持续直到完全发放
工作流的状态:
- 新建
- 批准或拒绝
- 部分
- 完全
这些是工作流中的书签。 我们已经讨论过,状态机工作流由用户决策驱动,并利用了这些书签。 书签控制工作流,并且在人为干预后再次恢复。 在此示例中,有两种情况:
活动
创建新的申购订单,例如“申购 5”
状态保存为 "NEW"
对于每个申购订单,都会创建一个新的工作流实例,此工作流实例将持久保存在数据库中。
工作流 GUID 保存在实例存储数据库中,显示书签并显示工作流状态的当前执行状态,在此查询输出中显示为“空闲”。
如果用户拒绝订单,则工作流将被终止。
如果用户批准订单,则工作流程继续
现在,批准工作流后,需要发放库存。 需要用户的第二次干预才能继续工作流程。 用户需要发放库存。 如果用户发放库存,则工作流程会继续,但是根据状态机流程图,如果发放是部分发放,则不会完成。 只有当用户发放全部库存时,工作才会完成
使用代码
代码详情
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();
}
}
}
结论
使用状态机和工作流引擎,开发人员可以创建一个流程来规范活动。