【问题标题】:State Machine WF: Issues with workflow termination状态机 WF:工作流终止问题
【发布时间】:2011-02-04 01:10:22
【问题描述】:
我们有一个状态机工作流程,用于维护用户提交的应用程序的状态。我遇到的问题之一与工作流终止有关。在其中一个州,我有一个错误。当应用程序达到该状态时,它会引发异常,结果,工作流的终止事件被调用,并且特定的工作流实例从持久性数据库中删除。所以我不能再加载那个工作流实例了。我希望,如果其中一个状态出现错误,会抛出异常(以便我们知道问题所在),但整个工作流实例不应该消失。故障处理程序活动能否确保工作流不会终止。另外,有没有办法,当调用终止事件时,实例不会从持久存储中删除。
感谢您的任何帮助/建议。
【问题讨论】:
标签:
workflow
workflow-foundation
state-machine-workflow
【解决方案1】:
当在工作流中抛出异常时,异常将有效地通过活动祖先“冒泡”,直到它是:
- 由故障处理程序活动处理
- 到达根活动(即:工作流本身),此时工作流将终止。
在正确的位置为所有可能的异常创建故障处理程序是一种很好的做法。使用故障处理程序活动,您可以恢复工作流实例,并有效地将状态机设置回可用状态,而不是让它终止。
一旦工作流终止,它就无法使用,因此一旦终止发生,将其保留在持久性存储中没有任何好处,这就是它被删除的原因。所以你可以看到,关键是要阻止它意外终止。
最后一个提示。当工作流终止时,导致终止的异常将作为 WorkflowTerminatedEventArgs 对象的 Exception 属性发送到事件侦听器。我绝对建议使用某种日志记录机制来捕获它并将其输出到某个地方,这样如果您将来遇到由于某种原因未被捕获的错误,追踪它们会容易得多。
【解决方案2】:
我创建了一个自定义 SQL 工作流持久性服务,它可以防止工作流实际终止 - 从而使工作流保持在导致错误的转换发生之前的先前状态:
public class CustomSqlWorkflowPersistenceService : SqlWorkflowPersistenceService
{
public CustomSqlWorkflowPersistenceService (string connectionString) : base(connectionString)
{
}
public CustomSqlWorkflowPersistenceService (NameValueCollection parameters) : base(parameters)
{
}
public CustomSqlWorkflowPersistenceService (string connectionString, bool unloadOnIdle, TimeSpan instanceOwnershipDuration, TimeSpan loadingInterval) : base(connectionString, unloadOnIdle, instanceOwnershipDuration, loadingInterval)
{
}
protected override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock)
{
WorkflowStatus workflowStatus = GetWorkflowStatus(rootActivity);
if (workflowStatus == WorkflowStatus.Terminated)
{
string workflowError = GetSuspendOrTerminateInfo(rootActivity);
if (!string.IsNullOrEmpty(workflowError))
{
string error = string.Format("Workflow terminated, forcing an abort! {0}", workflowError);
throw new Exception(error);
}
}
base.SaveWorkflowInstanceState(rootActivity, unlock);
}
}
如果您想强制终止 WF,这显然会很痛苦,但我相信您可以解决这个问题。
【解决方案3】:
我正在使用 SqlWorkflowPersistence,作为解决此问题的方法,我执行了以下操作:
在“WorkflowPersistence”数据库中,我们有删除实例的 SP“InsertInstanceState”。我注释了删除实例的代码。
看起来它可以工作,但我不确定这是否正确。
下面显示了我更改的部分 SP。
IF
@status=1 OR @status=3
BEGIN
/* DELETE FROM [dbo].[InstanceState] WHERE uidInstanceID=@uidInstanceID AND ((ownerID = @ownerID AND ownedUntil>=@now)
OR (ownerID IS NULL AND @ownerID IS NULL ))
*/
END