工作流系统中有很大一部分需要和人进行交互,有的时候需要很长的时间,所以我们不可能让工作流实例一直保存在内存当中,这就需要进行持久化操作。
工作流的持久化就是保存工作流的某些状态信息到持久化存储里,比如sql数据库,文件中,一旦被保存到持久化存储里了,工作流就可以从内存中移除掉,在需要的时候在进行装载。
持久化服务是WF中核心服务之一,WF框架提供一个标准的持久化服务SqlWorkflowPersistenceService,利用它可以将工作流状态信息存放在Sql Server数据库中,
你也可以自己实现持久化服务,你必须继承自WorkflowPersistenceService这个类,自定义的持久化服务可以把数据存储在二进制文件,xml,其他关系型数据库中等等,
但是一个工作流实例一次只能使用一个持久化服务。
一旦你将持久化服务加载到工作流引擎中,你就不需要手动去干涉了,他会自动的完成相应的操作。在以下状态的时候,工作流会被持久化。
1. idle的时候(如等待外部事件,使用DelayActivity)。
2. 工作流完成或终止。
3. 当TransactionScopeActivity完成的时候。
4. 当CompensatableSequenceActivity完成的时候。
5. 当一个装饰有PersistOnCloseAttribute的自定义活动完成的时候。
6. 当你手动的去调用Unload或TryUnload方法的时候。
如果你使用DelayActivity的时候,持久化服务也会存储DelayActivity的过期时间,并且持久化服务会定期检查是否过期,以准备从新恢复工作流状态,
SqlWorkflowPersistenceService有一个LoadingInterval属性可以设置获取加载间隔的长度。
有的时候当工作流变成idle的时候,你也可以选择不进行持久化存储,这种情况适合当你等待的外部事件比较频繁,而且事件很快就被接收的时候,
因为这个时候如果你在进行持久化存储,你花在卸载和装载上时间会更多,还不如不进行持久化存储呢。
下面我们完成一个例子来说如何使用SqlWorkflowPersistenceService:
1.首先我们需要建立我们的持久化数据库,WF已提供了响应sql脚本,位置如下:
[WindowsFolder]\Microsoft.Net\Framework\v3.0\Windows Workflow Foundation\SQL\[language].[windows]这里面有SqlPersistenceService_Schema.sql
和SqlPersistenceService_Logic.sql两个文件用来生成持久化数据库.
创建好的持久化数据库里包含两张表, InstanceState和 CompletedScope,InstanceState表中记录未完成事例的运行状态,CompletedScope表记录当工做流使用事务的支持。
{
private WorkflowRuntimeManager workflowManager;
private WorkflowPersistenceService persistence;
private PersistenceService persistenceService;
private Dictionary < Guid, Workflow > workflows = new Dictionary < Guid, Workflow > ();
private Workflow selectedWorkflow;
public Persistencecs()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base .OnLoad(e);
workflowManager = new WorkflowRuntimeManager( new WorkflowRuntime());
AddServices(workflowManager.WorkflowRuntime);
workflowManager.WorkflowRuntime.WorkflowCreated += new EventHandler < WorkflowEventArgs > (WorkflowRuntime_WorkflowCreated);
workflowManager.WorkflowRuntime.WorkflowCompleted += new EventHandler < WorkflowCompletedEventArgs > (WorkflowRuntime_WorkflowCompleted);
workflowManager.WorkflowRuntime.WorkflowPersisted += new EventHandler < WorkflowEventArgs > (WorkflowRuntime_WorkflowPersisted);
workflowManager.WorkflowRuntime.WorkflowUnloaded += new EventHandler < WorkflowEventArgs > (WorkflowRuntime_WorkflowUnloaded);
workflowManager.WorkflowRuntime.WorkflowLoaded += new EventHandler < WorkflowEventArgs > (WorkflowRuntime_WorkflowLoaded);
workflowManager.WorkflowRuntime.WorkflowIdled += new EventHandler < WorkflowEventArgs > (WorkflowRuntime_WorkflowIdled);
// initially disable these buttons until a workflow
// is selected in the data grid view
btnContinue.Enabled = false ;
btnStop.Enabled = false ;
// start the runtime prior to checking for any
// existing workflows that have been persisted
workflowManager.WorkflowRuntime.StartRuntime();
// load information about any workflows that
// have been persisted
RetrieveExistingWorkflows();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base .OnFormClosed(e);
// cleanup the workflow runtime
if (workflowManager != null )
{
workflowManager.Dispose();
}
}
void WorkflowRuntime_WorkflowCreated( object sender,WorkflowEventArgs e)
{
UpdateDisplay(e.WorkflowInstance.InstanceId, " Created " );
}
void WorkflowRuntime_WorkflowIdled( object sender,WorkflowEventArgs e)
{
UpdateDisplay(e.WorkflowInstance.InstanceId, " Idled " );
}
void WorkflowRuntime_WorkflowLoaded( object sender,WorkflowEventArgs e)
{
UpdateDisplay(e.WorkflowInstance.InstanceId, " Loaded " );
}
void WorkflowRuntime_WorkflowUnloaded( object sender,WorkflowEventArgs e)
{
UpdateDisplay(e.WorkflowInstance.InstanceId, " Unloaded " );
}
void WorkflowRuntime_WorkflowPersisted( object sender,WorkflowEventArgs e)
{
UpdateDisplay(e.WorkflowInstance.InstanceId, " Persisted " );
}
void WorkflowRuntime_WorkflowCompleted( object sender,WorkflowCompletedEventArgs e)
{
UpdateCompletedWorkflow(e.WorkflowInstance.InstanceId);
UpdateDisplay(e.WorkflowInstance.InstanceId, " Completed " );
}
private void RetrieveExistingWorkflows()
{
workflows.Clear();
// retrieve a list of workflows that have been persisted
foreach (SqlPersistenceWorkflowInstanceDescription workflowDesc in ((SqlWorkflowPersistenceService)persistence).GetAllWorkflows())
{
Workflow workflow = new Workflow();
workflow.InstanceId = workflowDesc.WorkflowInstanceId;
workflow.StatusMessage = " Unloaded " ;
workflows.Add(workflow.InstanceId, workflow);
}
if (workflows.Count > 0 )
{
RefreshData();
}
}
private void AddServices(WorkflowRuntime workflowRuntime)
{
String connStringPersistence = String.Format( " Initial Catalog={0};Data Source={1};Integrated Security={2}; " , " c6ps " , @" . " , " SSPI " );
persistence = new SqlWorkflowPersistenceService(connStringPersistence, true , new TimeSpan( 0 , 2 , 0 ), new TimeSpan( 0 , 0 , 5 ));
workflowRuntime.AddService(persistence);
// add the external data exchange service to the runtime
ExternalDataExchangeService exchangeService = new ExternalDataExchangeService();
workflowRuntime.AddService(exchangeService);
// add our local service
persistenceService = new PersistenceService();
exchangeService.AddService(persistenceService);
}
private delegate void UpdateDelegate();
/// <summary>
/// Update the status message for a workflow
/// </summary>
/// <param name="instanceId"></param>
/// <param name="statusMessage"></param>
private void UpdateDisplay(Guid instanceId, String statusMessage)
{
UpdateDelegate theDelegate = delegate ()
{
Workflow workflow = GetWorkflow(instanceId);
workflow.StatusMessage = statusMessage;
RefreshData();
// slow things down so you can see the status changes
System.Threading.Thread.Sleep( 1000 );
};
// execute the anonymous delegate on the UI thread
this .Invoke(theDelegate);
}
private Workflow GetWorkflow(Guid instanceId)
{
Workflow result = null ;
if (workflows.ContainsKey(instanceId))
{
result = workflows[instanceId];
}
else
{
// create a new instance
result = new Workflow();
result.InstanceId = instanceId;
workflows.Add(result.InstanceId, result);
}
return result;
}
private void RefreshData()
{
// setup binding for DataGridView
BindingSource source = new BindingSource();
dataGridView1.DataSource = source;
source.DataSource = workflows.Values;
dataGridView1.Columns[ 0 ].MinimumWidth = 220 ;
dataGridView1.Columns[ 1 ].MinimumWidth = 80 ;
dataGridView1.Columns[ 2 ].MinimumWidth = 30 ;
dataGridView1.Refresh();
}
private void UpdateCompletedWorkflow(Guid instanceId)
{
UpdateDelegate theDelegate = delegate ()
{
Workflow workflow = GetWorkflow(instanceId);
workflow.IsCompleted = true ;
};
// execute the anonymous delegate on the UI thread
this .Invoke(theDelegate);
}
private void dataGridView1_SelectionChanged( object sender, EventArgs e)
{
// save the selected workflow instance
if (dataGridView1.SelectedRows.Count > 0 )
{
DataGridViewRow selectedRow = dataGridView1.SelectedRows[ 0 ];
selectedWorkflow = selectedRow.DataBoundItem as Workflow;
SetButtonState();
}
}
private void SetButtonState()
{
if (selectedWorkflow != null )
{
btnContinue.Enabled = ! (selectedWorkflow.IsCompleted);
btnStop.Enabled = ! (selectedWorkflow.IsCompleted);
}
else
{
btnContinue.Enabled = false ;
btnStop.Enabled = false ;
}
}
#region 按钮事件处理
private void btnCreate_Click( object sender, EventArgs e)
{
workflowManager.StartWorkflow( typeof (PersistenceWorkflow), null );
}
private void btnContinue_Click( object sender, EventArgs e)
{
if (selectedWorkflow != null )
{
persistenceService.OnContinueReceived( new ExternalDataEventArgs(selectedWorkflow.InstanceId));
}
}
private void btnStop_Click( object sender, EventArgs e)
{
if (selectedWorkflow != null )
{
persistenceService.OnStopReceived( new ExternalDataEventArgs(selectedWorkflow.InstanceId));
}
}
#endregion
}
public class Workflow
{
private Guid instanceId = Guid.Empty;
private String statusMessage = String.Empty;
private Boolean isCompleted;
public Guid InstanceId
{
get { return instanceId; }
set { instanceId = value; }
}
public String StatusMessage
{
get { return statusMessage; }
set { statusMessage = value; }
}
public Boolean IsCompleted
{
get { return isCompleted; }
set { isCompleted = value; }
}
}