The SPWorkflowTask.AlterTask() method is handy dandy. It allows us to alter task details from outside of the workflow itself. I was using it in a couple of custom task forms I had created to update the task based on some user input. It’s pretty simple. You get a reference to the active workflow task item, create a hashtable that you populate with the data you want to change, then use AlterTask() to update the task.
This is pretty much exactly what I was doing, except I threw in an item.Update() after my SPWorkflowTask.AlterTask() call for good measure. I wasn’t sure if the AlterTask() actually did an Update() internally so I figured it couldn’t possibly hurt, right?
My workflow is a two-stage workflow. First one task gets assigned, and upon completion a new task is created and assigned to a different person. When I was testing, the first task would work fine but when I got to the second one I would always get the following exception:
This task is currently locked by a running workflow and cannot be edited
Luckily I have seen this before. The last workflow I worked on was built by a consultant that we flew in from out of town. At certain times in said workflow we encountered the same error. I looked into the fix I implemented for that issue and it turns out it was caused by a double Update() on the task item.
Long story short: Essentially he was calling a method that did some work on an item and called Update() before returning. Then immediately after the method call, he called Update() on the item again. Because the method was called in many different places, but the Update() after the method call only occured in this one particular spot, it was the only time it triggered this oddity. Oh, and it was only reproducible in one of our three environments.
So why would an Update() call followed by another one immediately after cause the workflow to lock? I don’t know. Maybe it’s still updating the database when the second one comes around, and it gets stuck in some sort of weird zombie state.
Looking at my code, there wasn’t really much to it. I created a hashtable which I populated with some data, passed it into the AlterTask() method along with a reference to the task item, and finished off by calling Update() on the item. Seemingly innocent enough…
Hashtable taskHash = new Hashtable();
taskHash[RecStrings.RecApprovalComment] = this.approvalCommentTextbox.Text;
taskHash[RecStrings.RecIsApproved] = button.Text == "Approve";
taskHash[SPBuiltInFieldId.TaskStatus] = "Complete";
taskHash[SPBuiltInFieldId.PercentComplete] = "1";
SPWorkflowTask.AlterTask(this.tasklistItem, taskHash, true);
this.tasklistItem.Update(); // Don't do this! It will cause problems...
Full of suspicion I fired up dotPeek and had a look at the AlterTask() method.
Turns out it does call Update(). Which means I was having the same issue that the other workflow was having. In hindsight, I guess that makes plenty of sense. If you have a method that changes an item, you’d expect it to actually finish off with an update to persist those changes.
The peculiar thing is that it only manifests itself once you’ve created a second task. If your workflow is a single stage with just one workflow tasks, the workflow will happily complete successfully.
After removing the this.tasklistItem.Update(); line in my code, the error happily went away.