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.]
Oh Rob. I can you help you, and solve all of your workflow problems. :)
Thanks for your blog. I have the same problem and the link to download the code does not work. Could you please send me a copy of the code on PersistOnClose?
Sorry, I fixed the URL now.
Hi Rob, Thanks for the blog.
I am trying to create Document Approval Workflow. I am stuck while trying to send email to approver (on task create) along with the link to the task as the task does not exist.
I added PersistOnClose activity after CreateTask and it is still not creating tasks after executing PersistOnClose.
My CreateTask activity is in a Sequence Activity within a Replicator activity.
Any idea where could it go wrong?
I had a similar problem & the way i fixed this by using OnTaskChanged Activity instead of OnTaskCreated. The trick is to use the ITaskService interface & use TaskUpdate eventreceiver to simiply call SystemUpdate() & it will immediately hydrate the work-flow instance to next activity (eg: sendmail activity to get the newly created taskItem). I tested this on SharePonit 2007 & 2010 works nicely & none of my work-flows instances failed. Until I nearly have more than 8000 workflow instances completed & 485 in progress. (Note: Dont forget to bump up the throttle value to 500 if you expect the workflowbeing used heavily.) Believe me, to resolve this issue I spend many sleepless nights & finally I managed to fix this issue with no latency
Rob, good stuff, but I’m stuck on a couple of things you mention with your work around that I hope you can help me out. Can you provide a step by step on creating this activity. You give the code, but I don’t know how to implement it since I’m new to sharepoint workflow development. I have a SharePoint 2007 Sequential Workflow project that is using parallel approval for an infopath form. Works well on my vm, but once I deploy to another enviroment it hangs during the approval part. I currently use the CreateTask, OnTaskCreated, OnTaskChanged (in a While activity), and CompleteTask activities to do a task approval (got the example from Robert Shelton’s video; http://rshelton.com/archive/2007/11/21/how-to-video-building-a-basic-approval-workflow-with-sharepoint.aspx). Please advise.
Thank you Robert for posting this solution. I spent countless hours looking for a workaround to use instead of OnTaskCreated. An empty sequence activity with the PersistOnClose activity worked great.