donderdag 27 oktober 2011

Nintex Workflow 2010 bug: 'Value does not fall within expected range'

Installed version: Nintex Workflow 2010 (2.2.3.0)
I was recently installing a solution on the client's farm. Part of the solution was a collection of Nintex workflows. When I imported a workflow and tried to publish it, I received the following message (this also happened with very simple '1 action workflows' that were newly created).


After doing tests for several hours we concluded it had to do with the number of items in the library the workflow was imported in. The default threshold for lists is 5000 items. We had like 8200 items, so more than the configured threshold. When increasing the threshold above 8200, the importing and publishing went just fine.

Of course this has some implications and therefore I cannot really agree with how the Nintex code is written (did some reflector digging). According to Nintex this has to do with SharePoint limitations and I really feel this is not a correct answer. First of all, the 5000 limit threshold has to do with how many items a view in a list can return. It says nothing about how many items you can have in the list itself. Even after creating views and making sure not one view returned more than the 5000 limit threshold, the problem would persist.

It was on this moment I decided to take a look into their code to find out what is going on. The stack trace was pointing to the following functions:

   1: private static SPFolder GetMetadataFolder(SPList list)
   2: {
   3:     SPFolder result = null;
   4:     if (list.BaseType == SPBaseType.DocumentLibrary)
   5:     {
   6:                     result = list.RootFolder.SubFolders["Forms"];
   7:     }
   8:     else
   9:     {
  10:                     result = list.RootFolder;
  11:     }
  12:     return result;
  13: }
  14:  
  15: public static AutoStartRuleCollection LoadFromList(SPList list)
  16: {
  17:     SPFolder metadataFolder = AutoStartRuleCollection.GetMetadataFolder(list);
  18:     return AutoStartRuleCollection.ReadRulesFile(metadataFolder, "Nintex_AutoStartRules.xml");
  19: }
  20:  
  21: private static AutoStartRuleCollection ReadRulesFile(SPFolder folder, string filename)
  22: {
  23:     SPFile file = folder.ParentWeb.GetFile(SPUrlUtility.CombineUrl(folder.Url, filename));
  24:     if (!file.Exists)
  25:     {
  26:                     return null;
  27:     }
  28:     byte[] bytes = file.OpenBinary();
  29:     string serialised = Utility.ConvertByteArrayToString(bytes);
  30:     return AutoStartRuleCollection.Deserialize(serialised);
  31: }

The exception itself is thrown by line 6 of the first function. Because the ‘SubFolders’ property is used, in the background the threshold limit is kicking in. What they actually try to do here is construct an URL to a file called ‘Nintex AutoStartRules.xml’ and for doing this, they use the SPFolder class. The file is stored in the hidden ‘Forms’ folder in document libraries and in the root for custom lists.

I have tried other ways to get a hold of this ‘Forms’ folder without triggering the threshold limit but came to the conclusion it’s not possible (as far as I know). But when looking at the bigger picture (they just want the construct the URL to that file), we could conclude that the URL can be easily constructed without the need of the SPFolder class which would make the solution work in all circumstances because the xml file always has the same name and the location is known upfront based on the type of the list.

woensdag 26 oktober 2011

Creating a WCF service in SharePoint 2010 using JSON

Recently I was trying to create a REST WCF service which uses JSON and although there are a lot of articles that talk about it, I couldn’t find one which contained a totally working example for SharePoint 2010. So to spare you some hours of frustration, I will share this information with you Smile .

1. Download the CKS tools
Because Visual Studio 2010 doesn’t have out of the box templates to quickly develop a WCF service, I downloaded and installed the CKS tools.

2. Create WCF Service
In your project, add a new WCF service from the CKS templates. You will notice that the template will always create an interface and a SVC + code behind file.

Untitled

3. Add your method prototypes to the interface
Open the interface file and add prototypes for each of your service methods.

   1: namespace MyNamespace
   2: {
   3:     // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ISubscriptionService" in both code and config file together.
   4:     [ServiceContract]
   5:     public interface IWCFService1
   6:     {
   7:  
   8:         [OperationContract]
   9:         [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
  10:         MyMethodAnswer MyMethod(string p1, int p2);
  11:  
  12:     }
  13:  
  14: }

What’s the most important here is the WebInvoke tag. It specifies the method uses POST, that the JSON format is used to accept and return data and that the sent data must be wrapped in a wrapping element (more about that later).

4. Edit the SVC file


By default it points to the code behind file. Let’s change it to the full assembly name. I also got rid of the Factory property.

   1: <%@ ServiceHost Language="C#" Debug="true" 
   2: Service="MyNamespace.MyClass, MyAssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d373e6657f243d25" %>

5. Implement your methods.

As you can see, the method is returning a ‘MyMethodAnswer’ object. You can use this if you want to return more than just a primitive type. Just make sure that object can be serialized. These methods are implemented in the ‘MyClass’ class.

   1: public MyMethodAnswer MyMethod(string p1, string p2)
   2: {
   3:     
   4:     // Implement
   5:  
   6: }

6. Prepare the web.config

I always create a separate web.config which holds the WCF service’s configuration. It’s better to deploy the SVC file in it’s separate subfolder in the ISAPI folder (automatically done by Visual Studio). The important thing is to also deploy the web.config there. Therefore I also create an ISAPI/MyAssemblyName folder in the Visual Studio project. In there, I create the web.config.

   1: <?xml version="1.0"?>
   2: <configuration>
   3:  
   4:   <system.serviceModel>
   5:  
   6:     <!-- we need this to enable session -->
   7:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
   8:     <behaviors>
   9:       <endpointBehaviors>
  10:  
  11:         <!-- our configuration for rest relies on web http -->
  12:         <behavior name="RestBehavior">
  13:           <webHttp />
  14:         </behavior>
  15:  
  16:       </endpointBehaviors>
  17:  
  18:       <serviceBehaviors>
  19:         <behavior name="NewBehavior" >
  20:           <serviceMetadata httpGetEnabled="true" />
  21:           <serviceDebug
  22:             httpHelpPageEnabled="true"
  23:             includeExceptionDetailInFaults="true"
  24:           />
  25:         </behavior>
  26:       </serviceBehaviors>
  27:  
  28:     </behaviors>
  29:     <bindings>
  30:       <webHttpBinding>
  31:  
  32:         <!-- a selection of security bindings that you can use in the service registration below-->
  33:         <binding name="WindowsAuthenticationBasicHttpBinding">
  34:           <security mode="TransportCredentialOnly">
  35:             <transport clientCredentialType="Windows" />
  36:           </security>
  37:         </binding>
  38:         <binding name="NoSecurityHttpBinding">
  39:           <security mode="None">
  40:             <transport clientCredentialType="None" />
  41:           </security>
  42:         </binding>
  43:  
  44:       </webHttpBinding>
  45:  
  46:     </bindings>
  47:     <services>
  48:  
  49:       <service name="MyNamespace.MyClass" behaviorConfiguration="NewBehavior">
  50:         <endpoint address=""
  51:                 binding="webHttpBinding"
  52:                 behaviorConfiguration="RestBehavior"
  53:                 contract="MyNamespace.IMyClass"
  54:                 bindingConfiguration="WindowsAuthenticationBasicHttpBinding">
  55:         </endpoint>
  56:       </service>
  57:  
  58:     </services>
  59:  
  60:   </system.serviceModel>
  61:  
  62: </configuration>
First of the all the web.config contains information about how the end point and the web service behave. This is defined in the behavior tags. Please note that the behaviors are used in the service definition.

In the bindings we define two authentication methods. The first one is the standard Windows authentication. The second one has no authentication.

Last thing is the service itself. The name itself doesn’t really matter. Just make sure you correctly reference the behavior configuration in the service and endpoint tag.


Make sure the ‘contract’ property is correctly set. This must point to your interface class in the correct namespace.

7. Deploy

What’s left right now is packaging and deploying it. You can then surf to the SVC file using your browser. If everything is right, you will see a page that starts with

   1: You have created a service.
   2:  
   3: To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:
   4:  

and what follows is some info about how to talk with your service.


8. Test the communication

Building the service is one thing, communicating with it is something else. It’s not always clear what exact data the service expects and what kind of data it will send back. Here are some tips to help you:

- In the same folder as the SVC file, create an html file with some simple JQuery:

   1: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
   2: <html>
   3:     <head>
   4:         <title></title>
   5:         <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
   1:  
   2:     </head>
   3:     <body>
   4:     
   5:         <script>
   6:  
   7:             jQuery.support.cors = true;
   8:  
   9:             var url = 'http://myserver/_vti_bin/mySubFolder/MyService.svc/MyMethod';
  10:  
  11:             $.ajax({
  12:                 url: url,
  13:                 dataType: "json",
  14:                 type: "POST",
  15:                 data: '{ "p1": "Parameter1", "p2": "54321" }',
  16:                 contentType: "application/json; charset=utf-8",
  17:                 dataFilter: function (data) { return data; },
  18:                 success: function (data) {
  19:                     alert('success');
  20:                 },
  21:                 error: function (XMLHttpRequest, textStatus, errorThrown) {
  22:                     alert(errorThrown);
  23:                 }
  24:             });
  25:  
  26:         
</script>
   6:  
   7:     </body>
   8:  
   9: </html>

This small piece of JavaScript is very powerful when you quickly want to test your WCF service. First, reference the jquery.js file (via the Internet or a local URL).

jQuery.support.Cors = true is meant to enable cross domain scripting. But not always necessary.

Next, specify the URL of the method you want to use.

And then finally, perform the Ajax call. It’s important you use and correctly specify all these parameters. Make sure that the dataType and contentType are correctly set. Also make sure the type is POST as we specified this earlier. Next big thing is the data property. As you can see, the parameter names are exactly the same as in C#.

If the call succeeds or fails, separate JavaScript is executed.

You can also use cUrl to make quick calls to the service but I will not go deeper into this here.

- Use the IE9 developer tools

They allow you to monitor the data that is sent and received so you can immediately see if everything is OK there. Do this by visiting your test HTML file, press F12, go to the ‘Network’ tab, click ‘Start capturing’. Then reload the test page, get back to the test HTML file and take a look at the POST entry.

If you click it, you can see the sent and received data in respectively the ‘Request body’ and ‘Response body’ tabs.

dinsdag 11 oktober 2011

SharePoint 2010 page layouts: hiding parts when not in editing mode

Just want to share a quick solution:

If you want to hide certain parts in your custom page layouts when not in editing mode you can use the following control:

   1: <PublishingWebControls:EditModePanel ID="EditModePanel1" class="ewiki-margin" runat="server">
   2:  
   3: </PublishingWebControls:EditModePanel>




Multiple of these controls are possible, just don’t forget to give them a separate ID.