Sharepoint Sequential Number Service

This custom web service resides alongside the built in Sharepoint web services. It is used in combination with a Sharepoint List which provides a central place to manage the sequential numbers of any application name passed as a parameter to the web service.

It’s a good solution for providing a sequential number to an InfoPath form. Setup a data connection in InfoPath to the web service to return the next available number when the user saves the form. It can also be called from a Sharepoint Designer workflow action to assign a sequential number to a list item, infopath form or document.

This walkthrough uses a standard C# ASP.NET Web Service Application created in Visual Studio.
Create a new ASP.NET Web Service Application in Visual Studio. We need to add a reference to the backend Sharepoint classes in the following dll located on the Sharepoint server:

c:\program files\common files\microsoft shared\web server extensions\12\isapi\microsoft.sharepoint.dll

If you’re not developing on the Sharepoint server, take a local copy of the dll to reference in your project.
Here’s the code listing:
using Microsoft.SharePoint;
using System.Configuration;
namespace NumberService
{
    /// 
    /// This web service returns a Sequential Number given an application name
    /// 
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public class SequentialNumber : System.Web.Services.WebService
    {
        /// 
        /// The NextValue method increments the number in the configured list and returns the next number.
        /// 
        /// The string name of the application to update./// A String containing the next available number
        [WebMethod]
        public string NextValue(string title)
        {
            double nextValue = 0;

            try
            {
                SPSite site = new SPSite("SPSiteUrl");
                SPWeb web = site.OpenWeb("/");
                bool allowUnsafe = web.AllowUnsafeUpdates;
                SPList numberList = web.Lists["Number Configuration"];

                foreach (SPListItem item in numberList.Items)
                {
                    if (item.Title == title)
                    {
                        try
                        {
                            web.AllowUnsafeUpdates = true;
                            nextValue = Convert.ToDouble(item["NextValue"]);
                            item["NextValue"] = nextValue++;
                            item.Update();
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                        finally
                        {
                            if (!allowUnsafe) web.AllowUnsafeUpdates = false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return nextValue.ToString();
        }
    }
}
The Sharepoint Site, List and Field are hardcoded in the example above, of course these should be moved to a configuration file.

We will need to install this assembly into the Global Assembly Cache (GAC) on the Sharepoint server, so it will need to be signed with a strong name. Edit the project properties and goto the Signing tab, check the “Sign the assembly” box and create a new strong name key file.

Build the project and copy the resulting dll file to into the c:\windows\assemblies folder on the Sharepoint server using windows explorer, this will install the assembly into the GAC. If you’re not working directly on the server you will not be able to copy the dll directly into the assemblies folder remotely, so copy it to a shared folder on the server first.

Make a note of the public key given to the dll file as it will be needed. The easiest way to do this is to right click the assembly in the c:\windows\assemblies folder and examine its properties, you can then copy the public key into the clipboard.

Next we need to install the web service onto the Sharepoint server alongside the existing built-in services. Copy the SequentialNumber.asmx file into the following folder on the Sharepoint server:

c:\program files\common files\microsoft shared\web server extensions\12\isapi

We need to add Disco and WSDL files to the same folder so the webservice can be discovered, this is where things get interesting… The built-in Sharepoint web services are available in all the sites for the Sharepoint web, the discovery files in the isapi folder are built on the fly for each Sharepoint site. This means they are not your standard Disco or WSDL files, but contain ASP markup to provide the virtualization functionality that is required to allow them to work with all the Sharepoint sites. If you deploy your web service to an IIS Server you can then download copies of the Disco and WSDL files which will need modifying before being saved to the Sharepoint folder.
<%@ Page="" Language="C#" Inherits="System.Web.UI.Page"%>
  <%@ Assembly="" Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Import="" Namespace="Microsoft.SharePoint.Utilities" %>
      <%@ Import="" Namespace="Microsoft.SharePoint" %>
        <% Response.ContentType = "text/xml"; %>
        <discovery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/disco/">
          <contractRef ref=""
          <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request) + "?wsdl"),Response.Output=""); %> docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> xmlns="http://schemas.xmlsoap.org/disco/scl/" />
          <soap address=""
          <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> xmlns:q1="http://tempuri.org" binding="q1:SequentialNumberSoap" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
          <soap address=""
          <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> xmlns:q2="http://tempuri.org" binding="q2:SequntialNumberSoap12" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
        </discovery>

Replace the namespaces, public key token with your settings.

Next create another new file called sequentialnumberwsdl.aspx and add the following code to it, again use the standard WSDL file as base:

<%@ Page="" Language="C#" Inherits="System.Web.UI.Page"%>
  <%@ Assembly="" Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Import="" Namespace="Microsoft.SharePoint.Utilities" %>
      <%@ Import="" Namespace="Microsoft.SharePoint" %>
        <% Response.ContentType = "text/xml"; %>
        <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://ianchivers.com/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://ianchivers.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
          <wsdl:types>
            <s:schema elementFormDefault="qualified" targetNamespace="http://ianchivers.com/">
              <s:element name="NextValue">
                <s:complexType>
                  <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="SPSiteUrl" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="SPWebUrl" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="ListName" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Title" type="s:string" />
                  </s:sequence>
                </s:complexType>
              </s:element>
              <s:element name="NextValueResponse">
                <s:complexType>
                  <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="NextValueResult" type="s:string" />
                  </s:sequence>
                </s:complexType>
              </s:element>
            </s:schema>
          </wsdl:types>
          <wsdl:message name="NextValueSoapIn">
            <wsdl:part name="parameters" element="tns:NextValue" />
          </wsdl:message>
          <wsdl:message name="NextValueSoapOut">
            <wsdl:part name="parameters" element="tns:NextValueResponse" />
          </wsdl:message>
          <wsdl:portType name="SequentialNumberSoap">
            <wsdl:operation name="NextValue">
              <wsdl:input message="tns:NextValueSoapIn" />
              <wsdl:output message="tns:NextValueSoapOut" />
            </wsdl:operation>
          </wsdl:portType>
          <wsdl:binding name="SequentialNumberSoap" type="tns:SequentialNumberSoap">
            <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
            <wsdl:operation name="NextValue">
              <soap:operation soapAction="http://ianchivers.com/NextValue" style="document" />
              <wsdl:input>
                <soap:body use="literal" />
              </wsdl:input>
              <wsdl:output>
                <soap:body use="literal" />
              </wsdl:output>
            </wsdl:operation>
          </wsdl:binding>
          <wsdl:binding name="SequentialNumberSoap12" type="tns:SequentialNumberSoap">
            <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
            <wsdl:operation name="NextValue">
              <soap12:operation soapAction="http://ianchivers.com/NextValue" style="document" />
              <wsdl:input>
                <soap12:body use="literal" />
              </wsdl:input>
              <wsdl:output>
                <soap12:body use="literal" />
              </wsdl:output>
            </wsdl:operation>
          </wsdl:binding>
          <wsdl:service name="SequentialNumber">
            <wsdl:port name="SequentialNumberSoap" binding="tns:SequentialNumberSoap">
              <soap:address location=""
              <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> />
            </wsdl:port>
            <wsdl:port name="SequentialNumberSoap12" binding="tns:SequentialNumberSoap12">
              <soap12:address location=""
              <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> />
            </wsdl:port>
          </wsdl:service>
        </wsdl:definitions>

The last thing we need todo is to edit the spdisco.aspx file which describes all the available Sharepoint web services, so that it also includes our Sequential Numbering service.
<discovery>
  <contractRef ref=""
  <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SequentialNumber.asmx?wsdl"), Response.Output); %> docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SequentialNumber.asmx"), Response.Output); %> xmlns="http://schemas.xmlsoap.org/disco/scl/" /><soap address=""
  <% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SequentialNumber.asmx"), Response.Output); %> xmlns:q1="http://schemas.microsoft.com/sharepoint/soap/directory/" binding="q1:SequentialNumberSoap" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
</discovery>
Finally restart IIS, then check that the service has is available by checking the following URL http://server/_vti_bin/sequentialnumber.asmx.

Now that the web service is installed we need to create a Sharepoint List that will store all the numbers. In the code example above the Site (SPSiteURL), List name (Number Configuration) and Field (NextValue) are all hardcoded. The Title column of the list should be a string that matches what will be passed into the web service, the NextValue field should be an integer column.

Put it all together and you should have a really next solution to sequential numbering in Sharepoint !

0 comments: