I’ve been working on a rather complex SharePoint workflow and I’ve run into a few problems. The workflow does a parallel approval of a form – and well, I’ve discovered a few issues.
First, there aren’t many examples of how to do parallel approvals. This is particularly true when you need to keep unique instance data per iteration of the replicator loop. However, by scoping the correlation token to the inner sequence activity in the replicator, using a custom sequence activity to hold the additional parameters you need, and using the ChildInitialized event of the replicator that can be done.
However, I also ran into some odd problems that only occur when a workflow is doing parallel execution. But before I get there, I have to explain how CreateTask works. The CreateTask activity doesn’t actually create a task immediately, it creates a request to create the task when the workflow is serialized. The basic thing is that they want to minimize disk IO on the SQL server so if you wanted to change things after creating it – but before it’s written to disk in the list – you could. However, there are many situations where you need to force the task to be created so you can start to use it in your loop conditions. Commonly you want to know if the item exists and isn’t completed. If you put this at the top of a while loop immediately after a CreateTask you’ll never enter the while loop because the task will be missing from the point of view of the condition.
To every problem there is a solution, enter the OnTaskCreated activity. But wait, I have to mention that Microsoft is recommending that you not use OnTaskCreated – if you don’t believe me check out KB 970548. They say to use DelayActivity. I’ve commened on DelayActivity in the past, particularly about the fact that you can’t have a DelayActivity that runs for less than a minute. Well, I was wrong. There’s a situation where you can have a DelayActivity fire in less than a minute.
If you’re running a parallel situation (say inside of a replicator) and you hit a DelayActivity the wait is put on a timer queue inside of the workflow – just like any other workflow foundation workflow. The other branches will continue to run while the DelayActivity quietly ticks off the time. So it is technically possible to have DelayActivity sit for less than a minute.
In my case, I had set my DelayActivity to one second and didn’t think anything about it. That is until I got a Null reference exception thrown back at me from the workflow. Why? Well, it seems like in the serialization process for the workflow the event fired and well, the SharePoint workflow host didn’t know what to do with it. (Reportedly this is fixed in SharePoint 2010 but I’ve not tested this.) So now what do I do?
Well, enter the PersistOnClose attribute. This is an attribute in the Workflow Foundation that signals to WF that the workflow should be serialized immediately after completing the activity. This sounds pretty good… I can do a create task then an activity that has this attribute and all is well. Of course, if I had 100s of parallel branches in a workflow this would be sort of abusive on the system forcing it to serialize a workflow 100s of times – but for my case where I’m only ever a dozen or so branches wide at the same time it works fine.
All I did was I created a new activity that does nothing – except it has a PersistOnClose attribute on it. I put this immediately behind my CreateTask and voila. I get my task created. There’s no crazy eventing going on. There’s no delay while the system goes to sleep and wakes back up – just a little extra overhead on the system.
Problem solved. It’s more than a bit crazy how you solve something like this – but it works and I’ve got one less problem to worry about.
[Update: You can download a copy of my code (use at your own risk) here.]