Developing JSLink SharePoint forms with workflows and associated issues – Part 1

I’ll start by saying I am not a fan of workflows, I find they generally introduce a lot of unnecessary problems, I much prefer to just develop a proper system with backend code and not some pointy clicky workflow BS.

However we can’t alway choose which technologies we develop a solution in, sometimes inappropriate technologies a thrust upon us from above.

I am a fan of JSLink though and in my opinion it’s a much better  forms solution in SharePoint than InfoPath ever was, I have seen some truly horrifying InfoPath forms in my time and I have been trying to talk people out of using InfoPath for years so I was very happy the day it was announced that InfoPath was deprecated.

Anyway I’ve developed a few solutions using JSLink and various workflow technologies now, so I’ve decided to write a bit about some of the approaches that I have found to work quite well, I’m going to break this up into a number of articles and the first topic will be how to handle workflow tasks.

How to handle with workflow tasks

So one approach I’ve found works is instead of directing the user to the workflow task, direct them to the form for the item the task is related to (the original list item), then once the user has done what they need to do in the original item complete the workflow task via a REST web service call.

Now when you use this approach it is important that you save the original list item before you complete the task items otherwise you will get save conflicts, and this is because when you complete the workflow task it will fire up the workflow which will then alter the item you are working on.

This is where it starts getting tricky, when you save the list item you will automatically get redirected to either the list view or the “Source” url preventing your from making the necessary REST calls to complete the workflow task.

So to get around this we need to override the default redirection behaviour of the form which works like this:

Once you have overridden the default redirect behaviour and saved the form your can now complete the workflow task like this:

Note: I have not included querying of the tasks list to find the associated tasks, you will need to do this to get the id of the associated task, in my example above I have hardcoded this id as 1.

Using this approach I find is much more intuitive for the user, and we are not using the workflow task to collect information from the user we are simply using the task to indicate that there is action required of the user, any data that is collected from the user is captured in the original list item where it is needed.

Note: In these examples I have not given much consideration to code structure and method breakdown, however these are very important elements of any application, and you should give careful consideration to this when developing your applications.

Load Workflow Class: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.

I ran into an interesting issue today on one of the sharepoint servers we manage, the issue was affecting all workflows across a single site collection, the workflows would just fail immediately and gave a ‘Failed to Start (Retrying)’ message, when I looked at the logs I found these errors:

Load Workflow Class: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.   
 at System.ThrowHelper.ThrowKeyNotFoundException()   
 at System.Collections.Generic.Dictionary`2.get_Item(TKey key)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.IsConfigForSite(SPSite site)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.GetWorkflowConfurationSection(SPSite site, String section)   
 at Microsoft.SharePoint.Workflow.SPWinOeHostServices.EnsurePluggableServices(SPSite site, SPWorkflowExternalDataExchangeServiceCollection services, ExternalDataExchangeService existingServices)   
 at Microsoft.SharePoint.Workflow.SPWinOeHostServices..ctor(SPSite site, SPWeb web, SPWorkflowManager manager, SPWorkflowEngine engine)     -
-- End of inner exception stack trace ---   
 at System.RuntimeMethodHandle._InvokeConstructor(Object[] args, SignatureStruct& signature, IntPtr declaringType)   
 at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)   
 at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.LoadPluggableClass(String classname, String assembly, Object[] parameters)

 

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.   
 at System.ThrowHelper.ThrowKeyNotFoundException()   
 at System.Collections.Generic.Dictionary`2.get_Item(TKey key)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.IsConfigForSite(SPSite site)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.GetWorkflowConfurationSection(SPSite site, String section)   
 at Microsoft.SharePoint.Workflow.SPWinOeHostServices.EnsurePluggableServices(SPSite site, SPWorkflowExternalDataExchangeServiceCollection services, ExternalDataExchangeService existingServices)   
 at Microsoft.SharePoint.Workflow.SPWinOeHostServices..ctor(SPSite site, SPWeb web, SPWorkflowManager manager, SPWorkflowEngine engine)     -
-- End of inner exception stack trace ---   
 at System.RuntimeMethodHandle._InvokeConstructor(Object[] args, SignatureStruct& signature, IntPtr declaringType)   
 at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)   
 at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.LoadPluggableClass(String classname, String assembly, Object[] parameters)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.GetService(SPWorkflowAssociation association, SPWorkflowEngine engine)   
 at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(SPWorkflow workflow, Collection`1 events, SPWorkflowRunOptionsInternal runOptions)

When I googled these errors I found this article and I tried his approach but no luck, so I opened up .NET Reflector and had a look at the IsConfigForSite method on the SPWorkflowManager class and found the following:

.net-reflector

The exception is being thrown when it’s trying to access the IisSettings dictionary using the SPUrlZone.Default enum, so I opened up a powershell console and had a look at what was in the IisSettings dictionary:

$site = Get-SPSite http://mysite
$site.WebApplication.IisSettings

This showed that there was no entry for the default zone in the IisSettings dictionary, initially I thought we just needed to reconfigure the alternate access mappings and add an entry for the default zone but this had no affect.

So I started discussing this issue with one of my colleagues who had worked on some issues with this sever the previous week. I knew he had made some pretty significant changes, and as it turned out one of those changes was he had extended the web application to the intranet zone and then deleted the previous web application.

This was the root cause of the problem, this is why there was no default entry in the IisSettings dictionary, so I extended the web application to the default zone and the workflows started working again.