A interesting question that came up last week was how to convert workflows defined in C# to XAML.
A co worker of one of the attendees of the Essential Windows Workflow Foundation 4 course had been experiencing a lot of problems with the workflow designer and decided to create their workflows in C# instead of using the designer to generate XAML. While these workflows run just fine you do lose the visual aspect of the designer, one of the benefits of workflow in the first place.
Fortunately it isn’t hard to save workflow objects, however they have been authored, into their XAML representation using the XamlServices.
Take the following workflow defined in C#.
var workflow = new Sequence();
workflow.Activities.Add(new WriteLine() { Text = "Hello workflow." });
workflow.Activities.Add(new Persist());
workflow.Activities.Add(new If()
{
Condition = new VisualBasicValue<bool>("System.DateTime.Now.Hour < 12"),
Then = new WriteLine() { Text = "Good morning" },
Else = new WriteLine() { Text = "Good afternoon" }
});
workflow.Activities.Add(new WriteLine()
{
Text = new VisualBasicValue<string>("\"The current time is: \" & System.DateTime.Now.ToLongTimeString()")
});
This will run just fine as is but if I want to convert this into XAML all I need is a single line of code:
XamlServices.Save(@"..\..\demo.xaml", workflow);
And when it runs I get a nice XAML file with the graphical representation of the original C# workflow.
And now I can continue enhancing the XAML version and run that instead.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
There are times when a workflow can’t be persisted safely using a SqlWorkflowInstanceStore. The reason isn’t so much saving the state of a workflow to disk, that could be done at any time, but the result when a workflow would be reloaded from disk in that state.
An easy example is a workflow handling a WCF request with a Receive and SendReply activity pair. Suppose you would save the workflow state after the message had been received but before the response had been send. No problem there. Now suppose the workflow is aborted just after the response is send and later the saved state is reloaded. The first action of the reloaded workflow would be to send the response again. But where? The connection is long gone and the client has seen the original response and is happy. Clearly this would fault the workflow again and for that reason the workflow is in a no persist zone starting at the Receive and ending at the SendReply activity.
But wait. The SendReply activity has a property named PersistBeforeSend, surely that means it will be persisted before the reply is send? Actually it doesn’t and it persists after the response has been send. So the property name is wrong? Not quite, although I believe it is poorly named, as the Persist activity is actually scheduled just before the response is send. But due to the asynchronous nature of workflow execution it doesn’t execute until after the response has actually been send and we are at a point where it would be save to reload the workflow.
So what happens of you try to persist a workflow in a no persist zone? You will get an exception with the following message:
Persist activities cannot be contained within no persistence blocks.
In this case all I did was drop a Persist activity between a Receive and SendReply activity pair, I didn’t even add or configure the SqlWorkflowInstanceStore itself.
Checking for a no persist zone
So how can we check if we are currently in a no persist zone? It turns out the NativeActivityContext has a property called IsInNoPersistScope that will tell us that, unfortunately the property is internal so we can’t use it [:(].
Turns out that duplicating this behavior is quite easy though. Whenever a no persist zone is started a NoPersistProperty is created and stored in the NativeActivityContext Properties collection. The NoPersistProperty is also internal but that is no problem as all we need to do if check if there is a object stored under the name "System.Activities.NoPersistProperty", something we can do with the following activity:
public class CheckForNoPersistZone : NativeActivity
{
public OutArgument<bool> IsInNoPersistScope { get; set; }
protected override void Execute(NativeActivityContext context)
{
var prop = context.Properties.Find("System.Activities.NoPersistProperty");
IsInNoPersistScope.Set(context, prop != null);
}
}
Creating our own no persist zones
Sometimes it might be required to stop a workflow from persisting and we can do so by creating our own no persist zone. There is no standard activity to do so but creating a pair of activities to do so isn’t hard. The basic building blocks are a NoPersistHandle and calling Enter() and Exit() on it to start and end the no persist zone. This could easily be done through a composite activity or through two separate activities. In the code below I have chosen for the approach with a single composite activities.
public class NoPersistZone : NativeActivity
{
private Variable<NoPersistHandle> NoPersistHandle { get; set; }
[RequiredArgument]
public Activity Body { get; set; }
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
NoPersistHandle = new Variable<NoPersistHandle>();
metadata.AddImplementationVariable(NoPersistHandle);
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
var noPersistHandle = NoPersistHandle.Get(context);
noPersistHandle.Enter(context);
context.ScheduleActivity(Body, OnCompleted);
}
private void OnCompleted(NativeActivityContext context, ActivityInstance completedInstance)
{
var noPersistHandle = NoPersistHandle.Get(context);
noPersistHandle.Exit(context);
}
}
Another nice activity to have for a generic toolbox.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu