工作流学习过程-持久化服务
工作流系统中有很大一部分需要和人进行交互,有的时候需要很长的时间,所以我们不可能让工作流实例一直保存在内存当中,这就需要进行持久化操作。
工作流的持久化就是保存工作流的某些状态信息到持久化存储里,比如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表记录当工做流使用事务的支持。
public partial class Persistencecs : Form
{
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; }
}
}