Introduction
This article I am writing for those who already knows developing in WWF
(Sequential Workflow), Here I am going to provide the solution on how to execute
same activity within a Sequential Workflow if there is uncertain failure. Why I
came to this solution because, I am seeing this problem since long time within
my project, and every time manual work was required to complete the
Orchestration of the workflow.
Background
If your workflow has many sets of activities which calls external/internal Web
Services/WCF and due to internal server error in those web services/wcf it was
raising FaultException (WebException) and your Workflow Activity was
failing/terminiting due to it, eventually your whole workflow was terminiting
due to that. Because this is how you have developed/designed your workflow to
stop there itself if there's any issue in an activity. But there are some
certain senario's where you don't want the compelete workflow to stop and you
may require that set of activity again execute itself to limited number of time
to see if it works or not (Replay of same request to web service), and if that
works, it should execute rest other activities in the workflow without
terminiting the workflow.
Understanding states of customer Activity
There are six states an activity can be in during its lifetime. These states are Initialized, Executing, Canceling, Closed, Compensating, and Faulting.
During the Initialized state, an ActivityExecutionContext
has been created for the activity and other initiation details specific to that activity have been executed. When an activity enters the Executing state, the primary functionality of that activity is performed. An activity is put into the Canceling state by a parent activity explicitly, or because an exception was thrown during the execution of that activity. The Closed state is the last and final state of an activity. The one caveat to this is if an activity successfully completes, but then must go through the Compensating state based on business logic. The activity will then transition from Closed to Compensating and then back to Closed once the compensation logic has completed.
The following flowchart shows how an activity transitions between the various activity states.
So basically on a happy path a activity go through Initialized, Executing and Closed.
Point of research
We know if an exception is thrown during the Executing state, Canceling state, or Compensating state of an activity, it will transition to the Faulting state.
Note: An activity cannot move from the Closed state to the Executing state.
Since we know once activity get closed it can not be moved to Executing state, but we can re-execute the activity before it close. Which mean that if for any reason activity get into Faulting State, it possible to move back it to Executing state.
Now, we know there is new transition can be possible here. Below image show new state transition in green line.
As we know each state of activity can be control by its predefined methods. Which we can override in our customer activity. Below are the two main override methods which i am going to use for my research.
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
How to get activity state to Executing from Faulting State?
We can achieve this by handling Fault inside the Activity. By overriding Activity state using HandleFault method we can know what is exectionContext and which type
of exception raised and to re-execute the activity we can call Execute Method which by passing exectionContext in it. But we have to be very careful in doing this and
we should make sure we are handling the replay properly with some logic, because it may takes you to deadlock condition if you don't do it.
Using the code
Here, I am going to show you using my sample POC project, how did i achieved it and you can also use same approach to achieve it.
Only thing which will differ for you; is that your logic/requirement of replay. We always need to make sure about the replay logic which should not go into endless loop.
Below is my project solution.
And in this I have below Sequential workflow, having three activities.
In CallWebServices activity i am trying to simulate this activity is calling web service and it may get Server Error which will raised server error fault exception,
resulting complete workflow will terminating due to exception raised by it.
Now, to solve the problem of complete workflow termination and again re-try
of executing same activity again from there itself so that if next re-try works and it gets success response from web service, the workflow should continue to complete
the orchestration.
Replaying => I am trying to achieve moving the activity state from Faulting to Executing
To achieve to the solution of this problem, I have added a BaseActivity which will be inherit in all of my workflow activities, like below.
public partial class Activity1: MyBaseActivity
Why I have though to write base activity because as per our requirement for the solution, we require each activity should have this feature for replaying.
Also I have added a dependency property to my base activity to set/limit the replay count.
So if any activity inheriting my base activity should set this property. Here in below pic i have set AllowedReplayCount to my CallWebServices Activity as 2,
which means this activity can re-play it self to max 2 times.
Below is code to add Dependency Property to my base activity
public static DependencyProperty AllowedReplayCountProperty =
DependencyProperty.Register("AllowedReplayCount", typeof(Int32), typeof(MyBaseActivity));
[DescriptionAttribute("AllowedReplayCount")]
[CategoryAttribute("Replay Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public Int32 AllowedReplayCount
{
get
{
return ((Int32)(base.GetValue(MyBaseActivity.AllowedReplayCountProperty)));
}
set
{
base.SetValue(MyBaseActivity.AllowedReplayCountProperty, value);
}
}
In this base activity I am handling the Fault raised by the activity by overriding the HandleFault
function.
This function gives us two important data; executionContext
and execption
. Using these data I have applied my logic of re-executing same activity.
And using dependency property I will be controlling the re-play will make sure it does not fallows into endless loop.
protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
{
if (exception is WebException && ReplayCount < AllowedReplayCount)
{
if (!exception.Message.Contains("404") || !exception.Message.Contains("409"))
{
Console.WriteLine("Faulted " + executionContext.Activity.Name);
ReplayCount++;
return Execute(executionContext);
}
}
return base.HandleFault(executionContext, exception);
}
Till now we see how to re-play the same activity by doing HandleFault function, but this is not it, we also require workflow to execute rest other activity after replay success for faulted activity. We require this because its basic feature of workflow; if any activity fault it terminate the complete workflow. So we have to stop Workflow to move into Faulting State from Executing State.
To achieve this situation, I have added faultHandlerAcitivity inside my base activity
And to support the replay, I have set the FaultType as System.Net.WebException
for faultHandlerActivity
. Also I have added codeActivity
.
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
CodeActivity senderActity = sender as CodeActivity;
FaultHandlerActivity faultException = senderActity.Parent as FaultHandlerActivity;
if (faultException.Fault is WebException)
{
if (ReplayCount > AllowedReplayCount)
{
throw faultException.Fault;
}
}
else
{
throw faultException.Fault;
}
}
I am using codeAcitivity to check if fault raised by activity fallow under replay condition or not. If its not part o my replay condition then only I am allowing
it to raise the fault to workflow.
That's all required to have this replay activity feature in a workflow.