One of the problems that I recently ran across was that I had a workflow that would stop getting events delivered to it while it was running. It wasn’t clear what was going on but with some help from Eilene Hao Klaka and Gabe Hall we were able to sort out the root issue. So let’s take a rather simple workflow:
The key here is that the workflow watches the item using OnWorkflowItemChanged then stops watching the item while it works on watching some tasks and then it resumes looking at the workflow. This looks pretty harmless and it is – right up to the point where someone makes a change to the document when the workflow is looking for task updates – but not item updates. When this happen the workflow gets the event, can’t process it – and ultimately it’s no longer runnable. It won’t respond to any more events or take any more actions. Making matters worse is that you don’t so much as get a message in the ULS that this has happened.
So what’s going on? The short of it is that the workflow host and workflow foundation believe that you need events on the item in the middle of this workflow because the scope of the correlation token for OnWorkflowItemChanged is the workflow itself. So even though there’s not an activity that is listening – the WF host and foundation believe there should be. The first thought might be to change the scope of the correlation token for OnWorkflowItemChanged to something smaller – but that won’t work because OnWorkflowItemChanged expects the token created at the scope of the workflow.
However, there is another approach that will work. You can setup a subscription for your own event by using the CallExternalMethodActivity, selecting the InterfaceType of Microsoft.SharePoint.Workflow.IListItemService and selecting the method name of InitializeForEvent. You’ll also need to provide an ID ([Updated]The guid from the workflowItem workflowProperties.Item.Guid), the itemId (which in our case we can get from workflowProperties.ItemId), and listId (again we can get this from workflowProperties.ListId). This sets up a subscription for events. The second activity we need is HandleExternalEventActivity. In this activity we select the same InterfaceType of Microsoft.SharePoint.Workflow.IListItemService and select an EventName of OnItemChanged. This will get signaled when the workflowItem is changed. The beauty of this is that you can place these two activities inside of a sequence activity and set the correlation token to the scope of the sequenceactivity. When the correlation token falls out of scope, the subscription for the events will automatically be removed.
I’ve bundled this into a custom sequence activity. The sequence activity that I’ve prepared will allow you to bind the SPWorkflowActivationProperties (workflowProperties) from which the activity will automatically get the listId and ItemId – or you can bind them individually. I’ve also allowed you to bind to the subscription id – while I don’t know why you would need this, I was trying to be complete. Finally, I also added a Invoked method so you can run code based on the event happening. CAUTION: I’m not setting the sender when I call your method so you’ll not want to use this for anything that requires the sender object. (i.e. for those cases when you need to know what branch you’re in.) This is the same problem that OnWorkflowItemChanged/OnTaskItemChanged has – so I didn’t see this as a big issue. The activity looks like this:
The code is available here – use it at your own risk. All you need to do is replace your OnWorkflowItemChanged with this activity and the rest of your workflow should remain undisturbed.
[Update 2010-07-27] There are a few issues with the initial post. I originally called out that the subscription ID be a random Guid — this isn’t correct, the Guid needs to be the guid of the document that the workflow is running against. Second, You need to make sure that you want the event that you’re subscribing for. If you quickly subscribe for an event and then exit the scope you’ll have problems. Finally, I adapted this approach to put the listen in the middle of a while loop to ensure that I wasn’t subscribing and falling out of scope repeatedly. Unfortunately, this adaptation can’t be packaged as an activity because of a bug in Visual Studio 2008 (the problem doesn’t exist in Visual Studio 2010). The bug prevents you from providing the condition to the while activity in your custom activity. The workaround is to simply replicate the pattern directly in the main workflow. Although this is tedious it works. — rlb