In my previous post “SharePoint 2010: Programmatically Approving/Rejecting Workflow” I mentioned the possibility of an issue when programmatically altering a workflow task. I have been testing it on several SharePoint 2010 farms with different parameters (different patch level, etc).
UPDATED July 2, 2011: RESOLVED
Set up
A simple SharePoint Designer 2010 workflow with the “Start Approval Process” action published to a document library. You start the workflow and a task is created for the approver.
The Approver is set to another test user, but all my code and script runs as the administrator (and thus System Account). Makes no difference though.
Note: when running the workflow multiple times (for testing), make sure to delete all tasks that are linked to your item. Or adapt the code below to not just take the first task in the collection…
Issue experienced
You alter the workflow task using a Visual Studio Console Application or Windows Application with the following (or similar code):
1 string url = "http://intranet/Shared Documents/Instructions.txt"; 2 using (SPSite site = new SPSite(url)) 3 { 4 SPWeb web = site.RootWeb; 5 SPListItem item = web.GetFile(url).Item; 6 SPWorkflowTask task = item.Tasks[0]; 7 8 Hashtable ht = new Hashtable(); 9 ht["TaskStatus"] = "Approved"; 10 11 SPWorkflowTask.AlterTask(task, ht, false); 12 }
After the code has run successfully you don’t see anything happen in the workflow status page. On some occasions you get “Due to heavy load, the latest workflow operation has been queued. It will attempt to resume at a later time” which could mean that the timer job hasn’t completed successfully yet. This message may or may not disappear for you.
Also, when trying to do a second alteration to the workflow task (by code or in the UI) you get “This task is currently locked by a running workflow and cannot be edited”.
So somehow your action was registered but not executed…
PowerShell to the rescue
Terminate the workflow (because it really hangs and there’s not much you can do with it) and kick off a new one.
Now open the SharePoint 2010 Management Shell (PowerShell) and run the following (or similar code):
$s = Get-SPSite("http://intranet")
$w = $s.RootWeb
$i = $w.GetFile("http://intranet/Shared Documents/Instructions.txt").Item
$t = $i.Tasks[0]
$ht = new-object Hashtable
$ht["TaskStatus"] = "Approved"
[Microsoft.SharePoint.Workflow.SPWorkflowTask]::AlterTask($t, $ht, $false)
This WILL work and correctly finish the approval cycle in any of my tested environments.
Notes
You can really test this over and over and over; it will work in PowerShell but not in the Visual Studio 2010 application. I have tested Release versus Debug, in and out Visual Studio, etc.
Solution (July 2, 2011)
The big difference in both techniques is that the PowerShell remains open and isn’t disposing any objects. This allowed the workflow engine to complete the task.
Option #1
1 SPWorkflowTask.AlterTask(task, ht, true);
From MSDN:
“If true is passed as the argument to fSynchronous, this method waits up to 30 seconds to determine whether the attempted update was accepted by the workflow schedule as valid. The method then returns true if the update was accepted and not rolled back, or false if the update was not accepted. If false is passed as the argument to fSynchronous, this method always returns true.”
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.workflow.spworkflowtask.altertask.aspx
I have found this working most of the times, however some test runs still experienced the issue. Perhaps my test environment isn’t powerful enough or the action takes just a little too long to kick in ?
Option #2
Using the code below I learned it took about 21 seconds for the task to complete:
1 int i = 1; 2 while (!(bool)item.Tasks[taskId]["Completed"]) 3 { 4 Console.Clear(); 5 Console.Write("" + i + " " + task["Completed"]); 6 Thread.Sleep(1000); 7 i++; 8 }
That should give ‘option #1’ suffice time to complete the task, but still it didn’t in all occasions. If you’re experiencing this you could write up your own delay either as replacement to, or in combination with ‘option #1’.
1 SPWorkflowTask.AlterTask(task, ht, true); 2 // Additional delay 3 while (!(bool)item.Tasks[taskId]["Completed"]) 4 { 5 Thread.Sleep(1000); 6 }
Related
Big kudos to my colleagues Tom De Wilde and Timmy Gilissen for helping me out !!