Reputation: 669
We have a state machine workflow for maintaining the state of an application submitted by a user. One of the issues I am having is related to workflow termination. In one of the states, I had a bug. When the application reached that state, it threw an exception and as a result, the terminate event of the workflow was called and the particular workflow instance got removed from the persistence database. So I am not able to load that workflow instance anymore. I would have hoped, if there is an error in one of the states, an exception would be thrown(so that we know what the issue is), yet the entire workflow instance should not disappear. Can the fault handler activity ensure that the workflow does not terminate. Also, is there a way, when the terminate event is called, the instances do not get removed from the persistence store.
Thanks for any help/suggestions.
Upvotes: 2
Views: 1223
Reputation: 11
I am using SqlWorkflowPersistence, as a fix for this I did the below:
In 'WorkflowPersistence' DB, we have SP 'InsertInstanceState' which deletes the instance. I commented the code which deletes the instance.
Looks like its working BUT I am not sure if this is correct way.
Part of the SP is shown below which I changed.
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
Upvotes: 0
Reputation: 2775
I created a custom SQL Workflow Persistence Service that prevents the workflow from actually terminating - thus leaving the workflow in the previous state before the transition that caused the error took place:
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);
}
}
This is obviously going to be a pain if you want to force a WF to terminate, but I'm sure you can work around that.
Upvotes: 0
Reputation: 3624
When an Exception is thrown in a workflow, the Exception will effectively "bubble up" through activity ancestors until it is either:
It is good practice to create fault handlers for all possible Exceptions in the right places. Using a fault handler activity you can recover the workflow instance, and effectively set the state machine back into a usable state rather than have it terminate.
Once a workflow has terminated it is unusable, so there would be no benefit to retaining it in the persistence store once the termination occurs, which is why it gets removed. So you can see, the key is to stop it terminating unexpectedly.
One final tip. When a workflow does terminate, the exception that caused the termination is sent to event listeners as the Exception property of the WorkflowTerminatedEventArgs object. I would absolutely recommend having some sort of logging mechanism to catch that and output it somewhere so that if you experience bugs in future that are for some reason uncaught, it will be much easier to track them down.
Upvotes: 2