FIX: SharePoint 2013 Workflow recursion prevention – Part 3
Following FIX: SharePoint 2013 Workflow recursion prevention – Part 1, this post will walk you through the process of designing your Visual Studio 2013 workflow that creates a list item on another list and invokes the workflow associated with that list using the new REST APIs released through SharePoint 2013 May 2014 CU as pointed out in part 1 of this series. If you haven’t read through part 1, I suggest you do to get some background that will help you appreciate this post.
The Scenario:
The scenario is the same as what’s provided in FIX: SharePoint 2013 Workflow recursion prevention – Part 2. Only difference is that these are workflows built using Visual Studio. We are going to take a look at how to leverage the new REST APIs to trigger off a workflow associated with a given list when item is created/updated in the list.
RECOMMENDATION: If you are using Visual Studio 2012 with Office Developer Tools, consider switching to Visual Studio 2013 with the latest Microsoft Office Developer Tools for Visual Studio 2013 – July 2014 Update (as on 22-August-2014). There were two problem I ran into using Visual Studio 2012 with Microsoft Office Developer Tools for Visual Studio 2012.
1) When re-deploying a workflow solution through Visual Studio 2012 after performing some changes, the changes won’t be reflected in the workflow instance (when it runs). This is because there’s perhaps some sort of file handle that’s not completely released when the process vssphost5.exe performs the deployment for us.
The workaround is to kill vssphost5.exe process before performing a re-deployment.
2) Got a weird error when I placed just the “CreateListItem” activity that simply creates an item on a list. The error is:
System.FormatException: Expected hex 0x in "{0}".
at System.Guid.GuidResult.SetFailure(ParseFailureKind failure, String failureMessageID Object failureMessageFormatArgument, String failureArgumentName, Exceptio innerException)
at System.Guid.TryParseGuidWithHexPrefix(String guidString, GuidResult& result)
at System.Guid.TryParseGuid(String g, GuidStyles flags, GuidResult& result)
at System.Guid.Parse(String input)
at Microsoft.Activities.Expressions.ParseGuid.Execute(CodeActivityContext context)
at System.Activities.CodeActivity`1.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
at System.Activities.ActivityInstance.Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)
The reason for this error and solution is documented is my good friend Chandrasekar’s blog post (thanks Chandru!). The context there is list lookup activities. But the resolution is the same because technically “CreateListItem” is also a sort of a list lookup activity on which we can create items.
Having said that let’s now look at our objective.
VSList1 has the workflow VSWF1 associated with it and it’s designed to create a list item on another list VSList2.
The “CreateListItem” activity is declared like shown below. It creates an item on VSList2 and it returns ItemId which is the integer value (list item ID – the sequence number). We create a new variable named NewItemId to store this value.
NOTE: This is one difference between a Visual Studio workflow and SharePoint Designer workflow when we try to create an item. While the return value we get from “CreateListItem” activity is an integer value, the return value from “Create List Item” is the GUID of the item that got created.
For the actual list item, we simply write some text to the title property as shown below.
On VSList2, we associate another workflow named VSWF2. This workflow simply writes “VSWF2 triggered successfully on item creation.” to history list as shown below.
NOTE: It’s not required that this workflow should be a Visual Studio workflow, it can be a SharePoint Designer workflow as well. Just to keep this post simple, I am using another VS workflow.
After deploying these workflows, we go to VSList1, create an item and start off VSWF1 manually on that item. We should see the following in workflow status page.
And then we visit VSList2 and see that the new item is created successfully.
The problem:
However, the /workflow.aspx page indicates that there were no VSWF2 instance ever run whereas it should have since we have setup VSWF2 workflow to auto-start when it is created/updated. This is the actual problem.
Fix:
We basically need to do the same things we did for SharePoint Designer Workflow (walk through here). We open VSWF1 solution and add a “BuildDynamicValue” activity. Set its Properties to what’s shown below.
We create a variable named “RespContent” of type “DynamicValue”. We won’t see the “DynamicValue” type in the dropdown immediately so we’ll have to choose “Browse for Types” and search for “DynamicValue” and pick it up as shown in the pictures below.
Assign the “ReqHeader” variable to the Result property of BuildDynamicValue activity.
Next we drop a “LookupWorkflowContextProperty” activity. We create a new variable named “SiteUrl” of type string. Click on “LookupWorkflowContextProperty” and in the properties windows of Visual Studio 2013, choose “Current Site URL” for “PropertyName” and set “Result” to the new string variable “SiteUrl” we just created as shown below.
We did this to pick up the current site’s URL that we need to use later for sending the HttpSend requests.
The first thing we need is the GUID of WFList2. To get that design a HttpSend method and get the GUID. Drop a HttpSend activity. Choose GET for the Method in the properties window of HttpSend activity. Set RequestHeaders property to ReqHeader, the variable we created above. For the Uri property provide the following value:
Hit OK. We create a new variable named ResponseContent of type DynamicValue and assign that to ResponseContent property in HttpSend activity. Now our HttpSend activity should look like the below.
We drop a “GetDynamicValueProperty<T>” activity next. When we get the small window to select the type for <T> we choose System.Guid (again use Browse for Types… and pick Guid by searching for it). Click on “GetDynamicValueProperty<T>” and set PropertyName field in the properties window to "d/Id" . For the Source field choose the variable ResponseContent that we used to store the output from the above HttpSend method call. Create a new variable named WFList2Guid of type System.Guid and set that as the value for Result field in “GetDynamicValueProperty<T>” properties window. Now we should have the workflow designed as shown below.
Now that we have the GUID of WFList2 stored in the variable List2Guid, we move forward to pick up the workflow subscription Id of the workflow we are interested in by calling /_api/SP.WorkflowServices.WorkflowSubscriptionService.Current/EnumerateSubscriptionsByList(guid'" + List2Guid.ToString() + "')?select=Id. This again is a HttpSend method so we go ahead and design it. The only think to note is that this will be a HTTP POST request. The completed design should look like below.
Next we drop two more “GetDynamicValueProperty<T>”. We design the first one (the type should be GetDynamicValueProperty<DynamicValue>) as shown below.
NOTE: As to how we decided to use “d/results(0)”, please review the part about using Fiddler to determine this in part 2. But for this walk through, we use Fiddler, replay the request to get workflow subscriptions on list VSList2 and then we choose the correct one. Below is the response we get from Fiddler.
The response shows we have 3 workflow subscribed to this list. But the one we need is the first one whose name is “VSWF2 – Workflow Start” which is at index 0. So we use “d/result(0)”. The 2nd and 3rd are the triggers to start this workflow when an item is added or updated.
We create a variable named VSWF2SubscriptionId of type System.Guid. And we design the second one (the type should be GetDynamicValueProperty<System.Guid>) as shown below.
Now we have both the parameters we need to call StartWorkflowOnListItemBySubscriptionId REST method. We’ll design our next HttpSend method to do exactly that. The Uri property of our HttpSend method will be what’s shown below.
The complete design of this final HttpSend method should look like what’s shown below.
We’ve just assigned the ResponseContent field to the ResponseContent variable we created above (we are reusing the same variable). But it actually does not matter for this REST method call. And that’s it!!
Re-deploy VSWF2 back to VSList1. We then browse to VSList1, create another item and start off VSWF1 on that item manually. Now we browse to VSList2 and we can see the new item that was created by VSWF1 workflow.
And now if we visit the workflow status page we should see the following.
If we click the Completed hyperlink, we should see the text we logged to the workflow history list.
I hope these walkthroughs posted in this series were helpful and gives everyone some basic insights into SharePoint REST APIs.
Tip:
Everyone should already know this, but a very quick way to find out SharePoint REST methods is by appending the $metadata attribute to the _api endpoint (e.g., https://sp/_api/$metadata). This will get us all REST methods and their input/output parameters, which I think will be quite helpful.
For e.g., I can check the method EnumerateSubscriptionsByList using this technique as shown below.
Happy exploring!
Comments
Anonymous
August 31, 2014
Great solution! It is important to emphasize that second WF has to have Manual Start Enabled set to true otherwise it will not work.Anonymous
August 31, 2014
Thanks Slaven! I pointed that out in part2 here: blogs.msdn.com/.../fix-sharepoint-2013-workflow-recursion-prevention-part-2.aspx. Since this was a series I didn't state that explicitly in this post too.Anonymous
November 24, 2015
Thanks for the valuable solution! Can we trigger Site Workflow rather than list workflow ? Thank you