Deploying Nintex Workflow Custom Action with Multiple Web Front End’s

Deploying Nintex Workflow Custom Action with Multiple Web Front End’s

After quite a break from doing any Nintex development I was required to write a custom Workflow Action for a client. I broke out the Nintex SDK to remind myself how to do this and wrote my action.

I debugged and tested the action on my dev VM and got it ready to deploy to the UAT environment. One key thing that is different about the UAT environment to my VM is the fact that it is a multi server farm with two WFE’s.

I wasn’t 100% sure that the code used in the Nintex SDK to add the action/activity as an AuthorizedType in the web.config would work with multiple WFE’s as it uses the SPWebConfigModification class.

A quick google turned up this post on MSDN – http://blogs.msdn.com/b/malag/archive/2009/05/22/spwebconfigmodification-does-not-work-on-farms-with-multiple-wfes.aspx.

Unfortunately it seems the code Nintex provide tries to get the Farm instance before applying the WebConfigModification – there is however a way around this – read on for more…

In the FeatureActivated event the following code is used to add the WebConfigModification -

[code lang="csharp"]
AuthorisedTypes.InstallAuthorizedWorkflowTypes(parent, action.ActivityAssembly, activityNamespace, activityTypeName);
[/code]

In order to get to the bottom of what this method did I decompiled the Nintex Workflow dll and looked for this method. The method uses the below code -

[code lang="csharp"]
public static void InstallAuthorizedWorkflowTypes(SPWebApplication webApp, string assemblyName, string namespaceName, string typeName)
{
AuthorisedTypes.assemblyName = assemblyName;
AuthorisedTypes.namespaceName = namespaceName;
AuthorisedTypes.typeName = typeName;
AuthorisedTypes.webApp = webApp;
SPSecurity.CodeToRunElevated secureCode = new SPSecurity.CodeToRunElevated(AuthorisedTypes.PerformWebConfigModificationForAuthorizedType);
SPSecurity.RunWithElevatedPrivileges(secureCode);
}

private static void PerformWebConfigModificationForAuthorizedType()
{
try
{
string value = string.Format("<authorizedType Assembly=\"{0}\" Namespace=\"{1}\" TypeName=\"{2}\" Authorized=\"True\" />", AuthorisedTypes.assemblyName, AuthorisedTypes.namespaceName, AuthorisedTypes.typeName);
SPWebConfigModification sPWebConfigModification = new SPWebConfigModification(string.Format("authorizedType[@Assembly='{0}'][@Namespace='{1}'][@TypeName='{2}']", AuthorisedTypes.assemblyName, AuthorisedTypes.namespaceName, AuthorisedTypes.typeName), "configuration/System.Workflow.ComponentModel.WorkflowCompiler/authorizedTypes");
sPWebConfigModification.Owner = "Nintex Workflow 2010";
sPWebConfigModification.Sequence = 0u;
sPWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
sPWebConfigModification.Value = value;
if (!AuthorisedTypes.webApp.WebConfigModifications.Contains(sPWebConfigModification))
{
AuthorisedTypes.webApp.WebConfigModifications.Add(sPWebConfigModification);
AuthorisedTypes.webApp.Update();
AuthorisedTypes.webApp.Farm.Services.GetValue&lt;SPWebService&gt;().ApplyWebConfigModifications();
}
}
catch (Exception innerException)
{
new NWException(NWResource.GetString("NWException_FailedToInstallAuthorizedTypes"), innerException);
}
}
[/code]

As you can see it uses the Farm instance to apply the WebConfigModification – this won’t work with multiple WFE’s. What I did was copy the method directly into my FeatureReceiver and modified it to use SPWebService.SPContentService.

Here you can see the final code -

[code lang="csharp"]
/// <summary>
/// Taken from the Nintex Workflow dll and modified - in multiple WFE environments you cannot get the farm instance - need to use SPWebService.ContentService
/// </summary>
/// <param name="webApp"></param>
/// <param name="assemblyName"></param>
/// <param name="namespaceName"></param>
/// <param name="typeName"></param>
public void PerformWebConfigModificationForAuthorizedTypeCustom(SPWebApplication webApp, string assemblyName, string namespaceName, string typeName)
{
try
{
string value = string.Format("<authorizedType Assembly=\"{0}\" Namespace=\"{1}\" TypeName=\"{2}\" Authorized=\"True\" />", assemblyName, namespaceName, typeName);
SPWebConfigModification sPWebConfigModification = new SPWebConfigModification(string.Format("authorizedType[@Assembly='{0}'][@Namespace='{1}'][@TypeName='{2}']", assemblyName, namespaceName, typeName), "configuration/System.Workflow.ComponentModel.WorkflowCompiler/authorizedTypes");
sPWebConfigModification.Owner = "Nintex Workflow 2010";
sPWebConfigModification.Sequence = 0u;
sPWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
sPWebConfigModification.Value = value;
if (!SPWebService.ContentService.WebConfigModifications.Contains(sPWebConfigModification))
{
SPWebService.ContentService.WebConfigModifications.Add(sPWebConfigModification);
SPWebService.ContentService.Update();
SPWebService.ContentService.ApplyWebConfigModifications();
}
}
catch (Exception innerException)
{
// Do something with this exception
}
}
[/code]

Take note the Nintex SDK also uses the Farm instance in the FeatureDeactivating method so you will need to change this to call the ContentService also.

Hope this helps someone Smile

*Update – I found this blog post (http://solutionizing.net/2009/05/26/spwebconfigmodification-works-fine/) which suggests that the ContentService class simply provides a constructor to handle if the Farm instance is null. Either way it is the recommended way to access the WebConfigModifications.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">