Export (0) Print
Expand All

Chapter 11: Building Customer Service Applications for the Right Situations (Real World SharePoint 2010)

SharePoint 2010

Summary: Learn about the new service application framework in Microsoft SharePoint Server 2010 and how to use it to create custom service applications.

Last modified: November 14, 2011

Applies to: Business Connectivity Services | SharePoint Foundation 2010 | SharePoint Server 2010 | Visual Studio

In this article
Introduction
Understanding Services in SharePoint
History of Services in SharePoint
SharePoint 2010 Service Architecture Framework
SharePoint 2010 Service Application Extensibility
Creating the Wingtip Calculator Service Application
Summary
Additional Resources
About the Author

This article is an excerpt from Real World SharePoint 2010: Indispensable Experiences from 22 MVPs, edited by Scot Hillier, from Wrox Press (ISBN 978-0-470-59713-2, copyright © 2010 by Wrox, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Contents

Click to grab code   Download code

Microsoft SharePoint 2010 introduces an improved (yet, somewhat new) concept to the SharePoint platform called the service application framework. Service applications are designed to replace SharePoint 2007 Shared Service Providers, and provide much more flexibility in terms of deployment, management, topology, and capabilities to the platform. One of the biggest changes is the capability for developers to build custom service applications. This chapter first looks at the history of service offerings for the SharePoint platform, and then introduces the new service application framework. It then addresses the subject of creating custom service applications, and walks through the process of creating one.

Note Note

Readers who are familiar with previous versions of SharePoint (SharePoint Portal Server 2003 and Office SharePoint Server 2007) may elect to jump straight to the section, SharePoint 2010 Service Architecture Framework. Readers who are familiar with the new service application framework and who want to dive into creating custom service applications can jump to the section, SharePoint 2010 Service Application Extensibility.

The sample service application referenced in this chapter is much too big to include all the accompanying code samples and files in the chapter itself. Instead, the most important parts have been included, with a considerable amount of code and things like error checking and validation having been omitted. Readers will likely get the most out of this chapter by downloading the code associated with this book from the companion website and following along while progressing through the chapter.

SharePoint has historically been a collaboration product to its core. All SharePoint users are familiar with the concepts of sites, lists, and libraries, as well as the various ways they can collaborate with each other though SharePoint. However, the story does not end there. Many versions ago, Microsoft saw the potential for SharePoint to be the hub of many corporate intranets. To facilitate this, some service offerings needed to be shared among many users.

The first services introduced in SharePoint (search, user profiles, audiences, and My Sites) were needed to extend across the boundaries of SharePoint sites. These services added value to the platform as a whole, rather than just to specific sites. With every release, SharePoint has become more popular and, thus, customers have demanded more and more evolution from Microsoft on the platform.

Those who are familiar with previous versions of SharePoint might want to skip the next section on the history of services in SharePoint and instead jump straight to the section, SharePoint 2010 Service Architecture Framework.

As with most products, having an understanding of the intent and reasons behind a capability or feature helps in gaining some perspective into how it works, as well as how it should be used. The following sections take a brief look back at the history of service offerings in the major SharePoint releases prior to SharePoint 2010, as well as the limitations of the approaches. Ultimately, the SharePoint 2010 service application framework was based off of the previous designs, including their advantages and disadvantages, as well as customer demand.

Service offerings (specifically, shared services) were first introduced in SharePoint v2.0, more commonly known as SharePoint Portal Server (SPS) 2003. Office SharePoint Server 2007 improved the architecture by adding a level of abstraction. The next two sections provide a brief look at services within these two products.

SharePoint Portal Server 2003

SPS 2003 was the first major release of SharePoint. Aside from the sites and site collections provided by Windows SharePoint Services (WSS) 2.0, SPS 2003 contained something called a portal. Portals could be seen as special super-site collections that took over an entire SharePoint web application. They offered federated search, a single user profile store to cache details on the user, audiences to target content to specific groups of users, and My Sites, which is a special site collection to which every user has access.

An SPS 2003 farm could contain a maximum of 20 portals. One challenge was that, because each portal had its own group of service offerings, multiple portals in one organization could cause confusion, because multiple search/user profile/audience stores would have to be maintained. In addition, users were quickly confused because it was not clear which My Site they were going to from each portal.

To address this, an SPS 2003 farm could be configured so that one portal in the farm could be flagged as the Shared Service Portal for the rest of the farm. Once this was done, all portals in the farm used the service offerings of the Shared Service Portal. This option was not ideal, because an SPS 2003 farm with 20 portals presented two choices: one set of services, or 20 configurations.

Office SharePoint Server 2007

Microsoft Office SharePoint Server (MOSS) 2007 included many changes to the service offering model in SharePoint that improved and expanded on the limitations of service offerings in SPS 2003. One big change was the removal of the portal construct from SharePoint; there were only site collections and sites. Another difference was that Microsoft removed the association of the services from specific web applications. Instead, all the service offerings were grouped together in something called a Shared Service Provider (SSP).

Administrators could then create a new SSP, hosted in its own web application, and configure the services within it individually. MOSS 2007 also introduced two new services: the Business Data Catalog (BDC) and Excel Services. When administrators created new SharePoint web applications, they configured them to use an existing SSP. Then, all site collections hosted by a given web application would leverage the service offerings in the SSP with which the web application was associated.

This model was a welcomed change to the service offering story in SharePoint, but it did have some limitations. First, it was an all-or-nothing proposition. If one web application simply wanted to use the same user profile store as another web application, but have a unique search configuration, administrators would have to create two separate SSPs, and simply duplicate the user profile configuration in the second.

Another limitation was that the service offerings were not extensible. There was no option for developers to create custom service offerings and deploy them in SharePoint. This was a common challenge for many organizations that wanted to share data or functionality across web applications. Many vendors simply created external stores and applications, and created custom controls or web parts that hooked into their systems.

SharePoint 2010's approach to service offerings is an even bigger change from MOSS 2007, as well as the change from SPS 2003 to MOSS 2007. First, Microsoft discarded the SSP construct and instead set up each service, now called a service application, independently. Web applications could now be associated with individual service applications, rather than a collection, thus removing the limitation in MOSS 2007 that forced administrators to create an entirely new SSP when only one service offering had a different configuration. This made SharePoint a much more multi-tenant-friendly solution, allowing multiple customers to run in the same SharePoint installation, rather than having a separate SSP setup for each customer.

Another big improvement is a significant level of abstraction and control over which servers host specific service applications. Service applications are installed on all servers in the farm, but administrators are free to configure each server independently (that is, they can specify which services will run on each farm). To do so, open Central Administration, and then click System Settings to display the Manage Services on Server page. When a service's status is set to Started, it can serve up requests or be executed on that application server.

Once a service is installed and configured to run on one or more application servers, an administrator then creates a new configuration of the service application. This configuration, a logical construct, is unique, and can run on any of the servers that have a Started instance running. Administrators are free to create multiple configurations of the same service application, such as in the case of search. One search configuration may be for confidential resources that should be available only to company employees. Another configuration may be available to vendors and customers. The web application that hosts the corporate intranet could then be associated with both search configurations, whereas a web application that is accessible by customers is only associated with the public search configuration.

One of the major abstraction points is that services are always consumed from the SharePoint Web Front End (WFE) servers, or those that run the web services and host the web-based experience for users. This is implemented through the use of service application proxies. A service application has an associated service application proxy that knows how to talk to the service application. All communication is done over Windows Communication Foundation (WCF) services via HTTP or HTTPS, which is very secure and does not require any special ports to be opened within or across farms.

This abstraction also enables service applications to be federated across SharePoint farms. As long as it has the proper service application proxies installed, and the necessary cross-farm trusts have been configured, one farm can connect to another farm's service applications. This enables administrators to create dedicated SharePoint farms built simply to host services for the rest of the organization.

Another interesting aspect of the service application framework in SharePoint 2010 is that it is built-in and part of SharePoint Foundation (SPF) 2010. Previous versions of SharePoint included service offerings only within the licensed versions of the product. Although SharePoint Server (SPS) 2010 does include quite a few service applications not found in SPF 2010, a few are included in SPF 2010. However, the important point here is that developers can build services that run on all SharePoint 2010 installations, not just the licensed versions customers have paid for.

Note Note

For more information about the SharePoint 2010 service application framework, refer to About service applications and services (SharePoint Server 2010) on TechNet.

Aside from all the other previously mentioned improvements to the service offerings in SharePoint 2010, another big change is that developers are now free to build their own custom service applications. As this chapter will demonstrate, it enables many vendors to create robust solutions that can snap right into SharePoint. One common example would be creating an anti-virus solution for SharePoint. This is a classic case of a service offering that all SharePoint sites would want to leverage, and administrators could easily configure it to run on individual application servers because it is a very processor-intensive task.

Building a custom service application is not an easy or trivial task. It takes quite a bit of time because there is a lot of plumbing involved, and many components must be built to satisfy the level of abstraction provided by the framework. The sample service application built in this chapter clearly demonstrates this.

The sample, the Wingtip Calculator Service, simply provides two operations: adding or subtracting two numbers. As shown in the chapter, quite a bit of work is done to implement this simple task. In addition, developers must have a solid understanding of not only the SharePoint API, but also the administration API, SharePoint administration web controls, advanced WCF programming, and consuming of services, as well as working with and creating custom Windows PowerShell cmdlets.

What the Service Application Framework Offers

Although there are quite a few things to build when creating a custom service application, the SharePoint 2010 service application framework has quite a bit to offer to developers who choose to build one. The following list includes many of the things that the service application framework has to offer to developers:

  • Application pool provisioning - Service applications are hosted within application pools in Internet Information Services (IIS). SharePoint can handle creating a new application pool and associating it with a custom service application, saving the developer quite a bit of work.

  • Database provisioning - Some service applications may need their own database or multiple databases. The framework includes a SPDatabase object that the provisioning process can pass back to SharePoint. The framework will create the SQL Server database, and run the specified T-SQL script to generate the schema.

  • Claims-based security support - SharePoint 2010 can support claims-based security, and the service application framework can also leverage it.

  • Backup/restore/upgrade support - Custom service applications can opt into the SharePoint farm backup/restore/upgrade process. Developers must only decorate specific service application objects with the correct attribute, and optionally implement a few methods, to partake in this process.

    Note Note

    For more information on backup/restore/upgrade support, refer to the SharePoint 2010 SDK on MSDN; specifically, Implementing Backup and Restore.

  • Out-of-the-box load balancer — The service application framework offers up a round-robin load balancer that spreads out the workload of each service application across all the application servers configured with a running instance.

  • Custom permission rights - Developers can even create custom permission rights, and grant those to specific users (or groups of users) to fine tune what things people can and cannot do with the service application.

Determining Whether or not to Build a Custom Service Application

Before building a custom service application, developers should take a step back and be sure that they have a solid understanding of when it does and does not make sense to build one. Building a custom service application involves creating multiple objects that inherit and extend provided objects in the SharePoint API. This is in addition to the work that must be done to implement the business logic of the service application itself.

Because of the amount of work involved, developers should evaluate the business requirements to decide if the benefits that the service application framework has to offer are worth the effort. Any business requirement that is unique to a specific site collection, site, or site template, it likely does not make sense to build a custom service application, nor one that does not share data across site collections or web applications.

The following lists some business requirements that would make more sense as a custom service application:

  • Those that share data across site collections or web applications (such as the Web Analytics or Managed Metadata service applications in SPS 2010)

  • Those that provide specialized calculations or analytics services (such as Web Analytics, Excel Services, or PerformancePoint Services)

  • Those that aggregate data (such as Search)

  • Those that are long-running or very intensive processes (such as Web Analytics, Search, or Word services)

  • Those that are used for middle-tier applications

At a high level, creating a custom service application can be broken down into a few different tasks. The first task is to create all the components and installers required to deploy those components to the SharePoint application servers. Next, the components and installers required to deploy these components to the SharePoint WFE servers must be created. Once these two core portions of the service application are deployed to the server, the next step is to create a consumer that will expose the service offering to users or other services. Finally, as with any custom development project, the last step is to test everything to ensure that it is working as desired.

The following sections detail how to create all the necessary service application components, as well as deploy and install them to the various SharePoint servers. Creating a custom service application involves creating quite a few classes and files. The service application created in this chapter is a very simple calculator, the Wingtip Calculator Service, providing addition and subtraction operations. This is a very simple example (and not much of a real-world example), but this approach will make it easier to follow the service application plumbing.

Note Note

Although this chapter includes some code snippets, a considerable amount of code has been omitted for readability. The complete Wingtip Calculator Service application is included in the code download that is associated with this book. Refer to the complete sample for all code that is required to implement the Wingtip Calculator Service application.

Configuring the Visual Studio 2010 Project

The new SharePoint Developer Tools in Microsoft Visual Studio 2010 do not include a project template or items that can serve as a starting point for creating a custom service application. Before beginning the process of creating a custom service application, take a few minutes to set up your project.

Start by creating a new project by using the Empty SharePoint Project template. When prompted by the SharePoint Customization Wizard, specify the local startup site to be the URL of the Central Administration site, and set it as a farm solution because service applications cannot be deployed to the sandbox.

The Empty SharePoint Project template adds only the most basic assembly references to the project. Building a custom service application requires adding some additional references. Add references to the following assemblies found on the .NET tab of the Add Reference dialog box.

  • System.Configuration

  • System.Management

  • System.Web

Two additional references are necessary, but they are not nearly as easy to add. Both will support creating custom Windows PowerShell cmdlets that will be used to create the command-line installer part of the custom service application.

The first one, System.Management.Automation.dll, is part of the Microsoft Windows Software Development Kit (SDK) and can be found in C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0. Developers can download and install the Windows SDK to get this file. However, this is a rather large install. Another option is to get this assembly from the server's global assembly cache (GAC). Unfortunately, it does not appear in the Add Reference dialog box. Because the Add Reference dialog box simply makes changes to the project file, developers can manually add this reference.

The other required assembly is the Microsoft.SharePoint.PowerShell.dll, which is also in the server's GAC, placed by there by the SharePoint installer.

To manually add the references to the project

  1. Right-click the project in the Solution Explorer tool window and select Unload Project. If prompted, save your project first.

  2. Right-click the project in the Solution Explorer tool window and then select Edit [...].csproj.

  3. Scroll down to the <ItemGroup /> section that contains a handful of <Reference /> elements and add the following entries.

    <Reference Include="System.Management.Automation" />

    <Reference Include="Microsoft.SharePoint.PowerShell"/>

  4. Right-click the project in the Solution Explorer tool window and select Reload Project. If prompted, save your project first.

With the references added, add a new farm-scoped Feature to the project. This will serve as the installer for the service application and service application proxy.

To add a new farm-scoped Feature

  1. Right-click the Feature node in the project in the Solution Explorer tool window and select Add Feature.

  2. Right-click the Feature1 node and rename it WingtipCalculatorServiceInstaller.

  3. Right-click the WingtipCalculatorServiceInstaller Feature, select View Designer, and then set the following values.

    • Name - Wingtip Calculator Service Installer

    • Scope - Farm

Now, because the SharePoint Development Tools do not include SharePoint Project Item (SPI) templates for items required by a service application, items must be created and placed in special folders. Thankfully, the SharePoint Developer Tools do provide a way to do this for just this reason.

Add the following mapped folders. Right-click the project in the Solution Explorer, click Add, and then select SharePoint Mapped Folder. (Repeat this process for each folder.)

  • \{SharePointRoot}\TEMPLATE\Admin - This will contain the pages used to create and configure the service application within the Central Administration site.

  • \{SharePointRoot}\CONFIG\POWERSHELL\Registration - This will contain the Windows PowerShell custom cmdlet registration XML files.

  • \{SharePointRoot}\WebClient - This will contain the configuration file required to make a connection from the WFE server to the service application's WCF service endpoint.

  • \{SharePointRoot}\WebServices - This will contain the WCF service endpoint for the service application.

Following generally accepted practices, add a subfolder under the Admin, WebClient, and WebServices folders named WingtipCalculatorService. Custom files should not be deployed alongside the SharePoint installation files. Therefore, keeping things separate with subfolders is a recommended practice. To make things easier to keep track of, rename the mapped folder Registration to PowerShellRegistration. This only changes the name of the folder in the Visual Studio project, not the target of where the files will be deployed.

There is one last step to perform to prepare the project. The SharePoint Development Tools build process performs a token replacement on various file types in the project for commonly used items. One of the tokens available to developers is $SharePoint.Project.AssemblyFullName$. At compile time, this token is replaced with the fully qualified four-part name of the generated assembly. By default only *.ASPX, *.ASCX, *.XML, *.WEBPART, and *.DWP files are searched for tokens to be replaced. This limited list is there only for performance reasons. Developers are free to modify their project files to instruct Visual Studio to include additional files.

Using the technique previously demonstrated to unload and reload a project file, add the following in a <PropertyGroup /> collection without a condition (such as immediately after the <SignAssembly /> element).

<TokenReplacementFileExtensions>svc</TokenReplacementFileExtensions>

At this point, the project is ready to host a new service application. The project should now look like Figure 1.

After showing you how to create the service application, the chapter walks through the process of creating two consumers. These should be located in different SharePoint packages (*.WSP), and, therefore, another project is required. Add a new project to the solution named WingtipCalculatorServiceConsumer by using the Empty SharePoint Project template, and repeat the following steps that were implemented on the service application project.

To add the new project

  1. When prompted by the SharePoint Customization Wizard, specify the URL of a SharePoint site collection to test a web part against, and set it as a farm solution.

  2. Add all the same references to the WingtipCalculatorServiceConsumer project that were added to the WingtipCalculatorService project.

  3. In the WingtipCalculatorServiceConsumer project, add an additional project reference to the WingtipCalculatorService project.

  4. Add a mapped folder named PowerShellRegistration to \{SharePointRoot}\CONFIG\POWERSHELL\Registration.

Figure 1. Visual Studio 2010 WingtipCalculatorService project

Visual Studio WingtipCalculatorService project

 

At this point, the project WingtipCalculatorServiceConsumer is ready to host the consumer components. The project should now look like Figure 2.

Figure 2. Visual Studio 2010 WingtipCalculatorServiceConsumers project

WingtipCalculatorServiceConsumer project

 

Now that you have created and configured the two Visual Studio projects, it is time to create the custom service application.

Creating the Application Server Components

The first portions of the service application to create are the parts that will reside on the SharePoint application servers. The bulk of the work that the service application does will be done on application server(s). These back-end components include the service program itself (the thing that does all the work), any associated timer jobs or databases, a WCF service endpoint that will expose the service operations to the SharePoint WFE servers, the administration interfaces (including a web-based user interface and command-line interface), and the installers.

Service

All service applications must have an implementation of the SPService object. This object is the center of gravity for the entire service application. This object should implement the SPIisWebService class (which inherits from SPService) and implement the IServiceAdministration interface. IServiceAdministration specifies all the members that are needed tell SharePoint some basic information about the service, such as its name (GetApplicationTypeDescription()), how to create a new instance (CreateApplication()), and how to create a proxy on the WFE servers to the service application (CreateProxy()), as well as the URL of the page (GetCreateApplicationLink()) administrators should be sent to when creating a new instance from the page Central Administration | Application Management | Manage Service Applications.

Create a new class named CalcService.cs in the WingtipCalculatorService project, and then add the code shown in Listing 1.

Listing 1. Creating the Service Object

public class CalcService : SPIisWebService, IServiceAdministration {
  public CalcService() { }
  public CalcService(SPFarm farm) : base(farm) { }

  public SPServiceApplication CreateApplication(string name, 
       Type serviceApplicationType, 
       SPServiceProvisioningContext provisioningContext) {
    // ... validation code omitted ...
  
    // if the service doesn't already exist, create it
    CalcServiceApplication serviceApp = this.Farm.GetObject(name, this.Id, 
         serviceApplicationType) as CalcServiceApplication;
    if (serviceApp == null)
      serviceApp = CalcServiceApplication.Create(name, this, 
           provisioningContext.IisWebServiceApplicationPool);

    return serviceApp;
  }

  public SPServiceApplicationProxy CreateProxy(string name, 
       SPServiceApplication serviceApplication, 
       SPServiceProvisioningContext provisioningContext) {
    // ... validation code omitted ...

    // verify the service proxy exists
    CalcServiceProxy serviceProxy = (CalcServiceProxy)this.Farm.GetObject
         (name, this.Farm.Id, typeof(CalcServiceProxy));
    if (serviceProxy == null)
      throw new InvalidOperationException("CalcServiceProxy does not 
           exist in the farm.");

    // if the app proxy doesn't exist, create it
    CalcServiceApplicationProxy applicationProxy = 
         serviceProxy.ApplicationProxies.
         GetValue<CalcServiceApplicationProxy>(name);
    if (applicationProxy == null) {
      Uri serviceAppAddress = ((CalcServiceApplication)serviceApplication).Uri;
      applicationProxy = new CalcServiceApplicationProxy(name, 
           serviceProxy, serviceAppAddress);
    }

    return applicationProxy;
  }

  public SPPersistedTypeDescription GetApplicationTypeDescription(Type 
       serviceApplicationType) {
    if (serviceApplicationType != typeof(CalcServiceApplication))
      throw new NotSupportedException();

    return new SPPersistedTypeDescription("Wingtip Calculator Service", 
         "Custom service application providing simple calculation 
         capabilities.");
  }

  public Type[] GetApplicationTypes() {
    return new Type[] { typeof(CalcServiceApplication) };
  }

  public override SPAdministrationLink GetCreateApplicationLink(Type 
       serviceApplicationType) {
    return new SPAdministrationLink
         ("/_admin/WingtipCalculatorService/Create.aspx");
  }
}

Service Interface/Operation Contracts

Before going much further the service contract that defines what operations the service application can implement needs to be created. This will define the public interface of the WCF service endpoint that the application proxies on the SharePoint WFE servers will use to communicate with the service instances running on the back-end servers. In the case of the simple Wingtip Calculator Service, this interface is quite simple. Add a new class named IWingtipCalcContract.cs to the WingtipCalculatorService project, and add the code shown in Listing 2 to it.

Listing 2. Wingtip Calculation Service Contract

[ServiceContract]
public interface IWingtipCalcContract {
  [OperationContract]
  int Add(int x, int y);

  [OperationContract]
  int Subtract(int x, int y);
}

Service Application

With the service contract defined, the service application can be created. This is the part that does all the work when prompted by the application proxies that reside on the various WFE servers in the farm.

This critical component tells SharePoint quite a bit about the service application. The service application can run on multiple application servers in the farm. It must run on at least one server in order to process requests, but it could run on multiple ones. One way to think of this is to compare it to web applications. A SharePoint web application runs on every single WFE server in the farm. Similarly, a service application can run on multiple application servers. Setting this up is covered later in this chapter.

A custom service application must implement two things: SPIisWebServiceApplication and IWingtipCalcContract (the service contract). This class is responsible for creating a new configuration of the service application (the Create() method) and telling SharePoint where to find different pieces that are associated with this service application.

The following list contains the properties that should be overridden from the SPIisWebServiceApplication class for the specific Wingtip Calculator Service implementation:

  • TypeName - This is the name of the service application as it should appear in the list of available services to create when clicking the New button in the ribbon on the Central Administration | Application Management | Manage Service Applications page.

  • InstallPath - This is the relative path where the service endpoint can be found on the application servers.

  • VirtualPath - This is the name of the WCF service endpoint file.

  • ManageLink - This is the Central Administration relative URL of the page administrators should be taken to when they select the service, or when they click the Manage button in the ribbon on the Central Administration | Application Management | Manage Service Applications page.

  • PropertiesLink - This is the Central Administration relative URL of the page administrators should be taken to when they click the Properties button with a service selected in the ribbon on the Central Administration | Application Management | Manage Service Applications page.

In addition, the service must implement the IWingtipCalcService interface, which, in this case, is quite simple. Listing 3 shows a sample of what should be in a new class file added to the WingtipCalculatorService project named CalcServiceApplication.cs.

Listing 3. Excerpt of Contents of CalcServiceApplication.cs

public class CalcServiceApplication : SPIisWebServiceApplication, 
     IWingtipCalcContract {
  public CalcServiceApplication()
    : base() { }
  private CalcServiceApplication(string name, CalcService service, 
       SPIisWebServiceApplicationPool appPool)
    : base(name, service, appPool) { }

  public static CalcServiceApplication Create(string name, CalcService service, 
       SPIisWebServiceApplicationPool appPool) {
    // ... validation code omitted ...

    // create the service application
    CalcServiceApplication serviceApplication = new 
         CalcServiceApplication(name, service, appPool);
    serviceApplication.Update();

    // register the supported endpoints
    serviceApplication.AddServiceEndpoint("http", SPIisWebServiceBindingType.Http);
    serviceApplication.AddServiceEndpoint("https", 
         SPIisWebServiceBindingType.Https, "secure");

    return serviceApplication;
  }

  public override string TypeName {
    get { return "Wingtip Calculator Service Application"; }
  }

  protected override string InstallPath {
    get { return SPUtility.GetGenericSetupPath(@"WebServices\Wingtip"); }
  }

  protected override string VirtualPath {
    get { return "CalcService.svc"; }
  }

  public override SPAdministrationLink ManageLink {
    get { return new SPAdministrationLink("/_admin/WingtipCalculatorService/
         Manage.aspx"); }
  }
  public override SPAdministrationLink PropertiesLink {
    get { return new SPAdministrationLink("/_admin/WingtipCalculatorService/
         Manage.aspx"); }
  }

  #region IWingtipCalcContract implementation
  public int Add(int x, int y) {
    return x + y;
  }

  public int Subtract(int x, int y) {
    return x - y;
  }
  #endregion
}

Service Endpoint

As previously explained, all inter-server communication in the service application framework occurs by using WCF services over HTTP or HTTPS. In order to expose the service application on the application servers to the service's application proxies installed on the SharePoint WFE servers, a WCF endpoint must be created. Because the CalcServiceApplication class already implements the IWingtipCalcService contract; all that is required is a simple WCF service that references this class.

Create a new text file named CalcService.svc in the WebServices\WingtipCalculatorService folder in the WingtipCalculatorService project with the following contents.

<%@ServiceHost Language="C#"
    Service="CriticalPath.SharePoint.Samples.WingtipCalculator.
         CalcServiceApplication, $SharePoint.Project.AssemblyFullName$" 
    Factory="CriticalPath.SharePoint.Samples.WingtipCalculator.
         CalcServiceHostFactory, $SharePoint.Project.AssemblyFullName$" %>

To expose this service, create another text file in the same location named web.config that will expose the service and define the service endpoints, bindings, and authentication mechanisms supported. Add the code in Listing 4 to the web.config file.

Listing 4. CalcService.svc web.config Contents

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="CriticalPath.SharePoint.Samples.WingtipCalculator.
            CalcServiceApplication">
        <endpoint binding="customBinding" 
             bindingConfiguration="CalcServiceHttpBinding" contract=
     "CriticalPath.SharePoint.Samples.WingtipCalculator.
     IWingtipCalcContract" address="" />
        <endpoint binding="customBinding" 
             bindingConfiguration="CalcServiceHttpsBinding" contract=
     "CriticalPath.SharePoint.Samples.WingtipCalculator.
     IWingtipCalcContract" address="secure" />
      </service>
    </services>
    <bindings>
      <customBinding>
        <binding name="CalcServiceHttpBinding">
          <security authenticationMode="IssuedTokenOverTransport" 
               allowInsecureTransport="true" />
          <binaryMessageEncoding>
            <readerQuotas maxStringContentLength="1048576" 
                 maxArrayLength="2097152" />
          </binaryMessageEncoding>
          <httpTransport maxReceivedMessageSize="2162688" 
               authenticationScheme="Anonymous" useDefaultWebProxy="false" />
        </binding>
        <binding name="CalcServiceHttpsBinding">
          <security authenticationMode="IssuedTokenOverTransport" />
          <binaryMessageEncoding>
            <readerQuotas maxStringContentLength="1048576" 
                 maxArrayLength="2097152" />
          </binaryMessageEncoding>
          <httpsTransport maxReceivedMessageSize="2162688" 
               authenticationScheme="Anonymous" useDefaultWebProxy="false" />
        </binding>
      </customBinding>
    </bindings>
  </system.serviceModel>
  <system.webServer>
    <security>
      <authentication>
        <anonymousAuthentication enabled="true" />
        <windowsAuthentication enabled="false" />
      </authentication>
    </security>
  </system.webServer>
</configuration>

Service Instance

Following Microsoft's practice of installing everything related to SharePoint on every server, and then configuring each server to serve a different role, custom service applications will be deployed to all servers in the farm using the SharePoint solution packaging infrastructure (*.WSP). Although the service applications are installed on every server in the farm, they do not necessarily run on all servers.

The service application framework enables administrators to designate the service applications to run on specific servers in the farm. This is done through the Central Administration | System Settings | Manage Services on Server page by starting or stopping instances. To get a custom service application to be listed on this page it must expose a service instance.

The service instance (a class that is derived from SPServiceInstance) associates an instance entry on the page in Central Administration with a specific service application. When a service application proxy requests the WCF endpoint of a service application as the location where it should send its instructions, SharePoint looks at all servers in the farm, and generates a list of those that have been flagged as having an online or started instance. It then picks one using a round-robin load balancer, and returns the address of the WCF endpoint on that server to the service application proxy.

Creating a new instance is very easy. First, create a new class named CalcServiceInstance that inherits SPIisWebServiceInstance and overrides a few properties, as shown in Listing 5.

Listing 5. CalcServiceInstance.cs

public class CalcServiceInstance : SPIisWebServiceInstance {
  public CalcServiceInstance() { }
  public CalcServiceInstance(SPServer server, CalcService service)
    : base(server, service) { }

  public override string DisplayName {
    get { return "Wingtip Calculator Service"; }
  }
  public override string Description {
    get { return "Wingtip Calculator providing simple arithmatic services."; }
  }
  public override string TypeName {
    get { return "Wingtip Calculator Service"; }
  }
}

At this point, all the necessary components required by the service application on the application servers in the farm have been created. The next step is to create a way for administrators to create and interact with the service application, as well as install and configure it in the farm.

Service Application Administration Pages

The SharePoint 2010 Central Administration interface provides a few ways for administrators to interact with the custom service application through a web browser interface. This is broken down into three main pages.

  • Create page - This page is used to create new configurations of the service application. This page is shown when the user selects the service application by clicking the New button in the ribbon on the Central Administration | Application Management | Manage Service Applications page.

  • Manage page - This page is used as the primary gateway to administer the service application. The recommendation is to treat this page like a dashboard page that provides details about the currently selected service application, and provides links to other pages that can be used to administer different pieces of the service application. This page is shown when the user selects the service application from the list of created service applications on the Central Administration | Application Management | Manage Service Applications page, and clicks the Manage button in the ribbon.

  • Properties page - This page is used to manage the general configuration of the created service application (such as changing its name, connecting to different databases, associating it with different application pools, changing the identity of an application pool, or something else specific to the service application). This page is shown when the user selects the service application from the list of created service applications on the Central Administration | Application Management | Manage Service Applications page and clicks the Properties button in the ribbon.

All of these pages (and associated pages they link to, in the case of the Manage page) should be deployed to the {SharePointRoot}\TEMPLATE\ADMIN folder on the server. The Manage and Properties pages are unique to the individual service application, and could contain anything. They are not used in the Wingtip Calculator Service because there is not a lot to configure.

However, the Create page is a bit more important because it contains some controls that Microsoft has created for creating an application pool for the administrator. The following code snippet demonstrates how to use the IisWebServiceApplicationPoolSection web control to create or select an existing application pool and then associate it with IIS.

<wssuc:InputFormSection Title="Name" Description="Specify a name for the 
     service application." runat="server">
  <template_inputformcontrols>
        <wssuc:InputFormControl LabelAssocatiedControlID="ServiceAppName" 
             runat="server">
            <template_control>
                <SharePoint:InputFormTextBox ID="ServiceAppName" class="ms-
                     input" Columns="35" runat="server" />
            </template_control>
        </wssuc:InputFormControl>
    </template_inputformcontrols>
</wssuc:InputFormSection>
<wssuc:IisWebServiceApplicationPoolSection ID="ApplicationPoolSelection" 
     runat="server" />

The code-behind for this page does three things:

  • It first creates a new instance of the service application and sets its status to SPObjectStatus.Online. This is the same thing as Started that is shown on the Manage Service Applications page in Central Administration.

  • It then creates a new instance of the service application on the current application server, and starts the instance by setting its status to Online as well. This is required because, for a service application to run, there must be at least one Online/Started instance on one application server in the farm.

  • The last step is to create a new instance of the service application proxy because that is how consumers will connect to the service application, including the administration part of the service application.

Refer to the project in the associated chapter code download for the complete code on how this is done.

Service Application Installer

Now that the service application has been created and administrators can interact with it, the last step is to install it in the farm. Because every service application is different, SharePoint does not have any included mechanisms to do this; this is something a developer must do for each custom service application. Installing the service application is a straightforward task that must be done through the SharePoint API.

Installation is broken down into the following parts:

  • First, a new instance of the service application (CalcService) must be created and added to the collection of services in the farm. This simply registers the service with SharePoint in much the same way as installing a Feature does not do anything except register the Feature for possible activation at the specified scope.

  • Second, a new instance of the service application proxy (CalcServiceProxy) must be created and added to the collection of proxies in the farm. Again, this makes SharePoint aware of a new application proxy.

Because this task must be done through code, many different options exist for performing this task, such as using a console application, Windows PowerShell, or an *.MSI installer. The easiest approach is to use a farm-scoped Feature and implement the installation and uninstallation logic in the FeatureInstalled() and FeatureUninstalling() methods. Because the Feature is farm-scoped, it is activated automatically when the Feature is deployed, making the entire deployment of the service application included in a single *.WSP file.

Listing 6 shows the code that should be added to the WingtipCalculatorServiceInstaller Feature previously created in the WingtipCalculatorService project.

Listing 6. WingtipCalculatorServiceInstaller Feature Event Handlers Installing the Service Application

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
  // install the service
  CalcService service = SPFarm.Local.Services.GetValue<CalcService>();
  if (service == null) {
    service = new CalcService(SPFarm.Local);
    service.Update();
  }

  // with service added to the farm, install instance
  CalcServiceInstance serviceInstance = 
       new CalcServiceInstance(SPServer.Local, service);
  serviceInstance.Update(true);
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
  // uninstall the instance
  CalcServiceInstance serviceInstance = 
       SPFarm.Local.Services.GetValue<CalcServiceInstance>();
  if (serviceInstance != null)
    SPServer.Local.ServiceInstances.Remove(serviceInstance.Id);

  // uninstall the service
  CalcService service = SPFarm.Local.Services.GetValue<CalcService>();
  if (service != null)
    SPFarm.Local.Services.Remove(service.Id);
}
Figure 3. Wingtip service application in Central Administration

Wingtip application in Central Administration

 

Once the solution package containing the service application and this Feature are deployed, the Feature will be activated, and the administrators should now see an entry in the New button on the Manage Service Application page, as shown in Figure 3.

Creating an Instance of the Service Application

With everything created and deployed, the next step is to create a new configuration of the custom service application. This can be done quite easily through Central Administration. But, by following Microsoft's SharePoint 2010 guidelines, administrators should also have the capability to perform this task using the command line.

The first step is to create a new service application configuration. This is done by first creating a new instance of the service application, granting a user rights to manage it, and then starting an instance of it. As previously mentioned, each service application is different, and, therefore, SharePoint does not include anything out-of-the-box. Instead, developers must implement this.

To do this, create a new Windows PowerShell cmdlet and register it with the SharePoint Windows PowerShell snap-in. Create a new class NewCalcServiceApplication.cs in the WingtipCalculatorService\Admin\WingtipCalculatorService folder, and have the class it creates inherit from SPCmdlet. When this cmdlet is called, it will call the InternalProcessRecord() method to execute, which will create the new service application shown in Listing 7.

Listing 7. NewCalcServiceApplication Windows PowerShell Cmdlet

[Cmdlet(VerbsCommon.New, "CalcServiceApplication", SupportsShouldProcess = true)]
public class NewCalcServiceApplication : SPCmdlet {
  #region cmdlet parameters
  [Parameter(Mandatory = true)]
  [ValidateNotNullOrEmpty]
  public string Name;

  [Parameter(Mandatory = true)]
  [ValidateNotNullOrEmpty]
  public SPIisWebServiceApplicationPoolPipeBind ApplicationPool;
  #endregion

  protected override bool RequireUserFarmAdmin() {
    return true;
  }

  protected override void InternalProcessRecord() {
    // ... validation code omitted ...

    // verify a service app doesn't already exist
    CalcServiceApplication existingServiceApp = 
         service.Applications.GetValue<CalcServiceApplication>();
    if (existingServiceApp != null) {
      WriteError(new InvalidOperationException("Wingtip Calc Service 
            Application already exists."),
          ErrorCategory.ResourceExists,
          existingServiceApp);
      SkipProcessCurrentRecord();
    }

    // create & provision the service app
    if (ShouldProcess(this.Name)) {
      CalcServiceApplication serviceApp = CalcServiceApplication.Create(
          this.Name,
          service,
          appPool);

      // provision the service app
      serviceApp.Provision();

      // pass service app back to the PowerShell
      WriteObject(serviceApp);
    }
  }
}

To register this cmdlet with the SharePoint Windows PowerShell snap-in, create a new XML file named WingtipCalServiceApplication.xml in the same folder as the cmdlet with the following contents. When the SharePoint Windows PowerShell snap-in loads, it will look at all XML files in the {SharePointRoot}\CONFIG\POWERSHELL\Registration and load the cmdlets listed in them.

<ps:Config xmlns:ps="urn:Microsoft.SharePoint.PowerShell"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:schemaLocation="urn:Microsoft.SharePoint.PowerShell 
                SPCmdletSchema.xml">
  <ps:Assembly Name="$SharePoint.Project.AssemblyFullName$">
    <ps:Cmdlet>
      <ps:VerbName>New-CalcServiceApplication</ps:VerbName>
      <ps:ClassName>CriticalPath.SharePoint.Samples.WingtipCalculator.
           NewCalcServiceApplication</ps:ClassName>
      <ps:HelpFile />
    </ps:Cmdlet>
  </ps:Assembly>
</ps:Config>

Now, to create the service application from PowerShell, open the SharePoint 2010 Management Shell (Start | All Programs | Microsoft SharePoint 2010 Products) and run the script shown in Listing 8. You can find a more verbose sample in the project in the code download that is associated with this chapter.

Listing 8. Windows PowerShell Script to Create a New Configuration of the Wingtip Calculator Service

write-host "Ensure service application not already created..." 
     -foregroundcolor Gray
$serviceApp = Get-SPServiceApplication | where { $_.GetType().FullName 
     -eq "CriticalPath.SharePoint.Samples.WingtipCalculator.
     CalcServiceApplication" -and $_.Name -eq "Wingtip Calculation 
     Service Application" }
if ($serviceApp -eq $null){
     write-host "Creating service application..." -foregroundcolor Gray
     $guid = [Guid]::NewGuid()
     $serviceApp = New-CalcServiceApplication -Name "Wingtip Calculation 
          Service Application" -ApplicationPool 
          "SharePoint Web Services System"
    if ($serviceApp -ne $null){
         write-host "Wingtip Calculation Service Application created." 
              -foregroundcolor Green
     }
}

write-host "Configure permissions on the service app..." -foregroundcolor Gray
$user = $env:userdomain + ‘\‘ + $env:username

write-host "  Creating new claim for $user..." -foregroundcolor Gray
$userClaim = New-SPClaimsPrincipal -Identity $user 
     -IdentityType WindowsSamAccountName
$security = Get-SPServiceApplicationSecurity $serviceApp

write-host "  Granting $user 'FULL CONTROL' to service application..." 
     -foregroundcolor Gray
Grant-SPObjectSecurity $security $userClaim -Rights "Full Control"
Set-SPServiceApplicationSecurity $serviceApp $security

write-host "Ensure service instance is running on server $env:computername..." 
     -foregroundcolor Gray
$localServiceInstance = Get-SPServiceInstance -Server $env:computername | 
     where { $_.GetType().FullName -eq "CriticalPath.SharePoint.Samples.
     WingtipCalculator.CalcServiceInstance" -and $_.Name -eq "" }
if ($localServiceInstance.Status -ne 'Online'){
     write-host "Starting service instance on server $env:computername..." 
          -foregroundcolor Gray
     Start-SPServiceInstance $localServiceInstance
     write-host "Wingtip Calculation Service Application instance started." 
          -foregroundcolor Green
}

The Wingtip Calculator Service application has now been created and configured on the application servers in the SharePoint farm. The next step is to create the Web Front End (WFE) components.

Creating the Web Front End Server (WFE) Components

The second part of building a custom service application is to create the components that will run on the SharePoint WFE servers. The two components that live on a SharePoint WFE related to service applications are the service proxy and application proxy. Once these two components are created, they must be installed and then provisioned.

Service Proxy

The service proxy acts as the hub for the service application on the SharePoint WFE server. The job of the service proxy is to tell SharePoint about the application proxy; that is, what type of service application(s) it can connect to, how to create a new instance of one, and what the user-friendly name is of the application proxy.

To create a new service proxy, add a new class CalcServiceProxy.cs to the proxy that inherits from SPIisWebServiceProxy and implements IServiceProxyAdministration. As previously mentioned, the IServiceProxyAdministration interface dictates that the proxy must provide specific details to SharePoint itself. The code in the service proxy is shown in Listing 9.

Listing 9. Creating the Service Proxy Object

public class CalcServiceProxy : SPIisWebServiceProxy, IServiceProxyAdministration {
  public CalcServiceProxy() : base() { }
  public CalcServiceProxy(SPFarm farm) : base(farm) { }

  public SPServiceApplicationProxy CreateProxy(Type 
       serviceApplicationProxyType, string name, Uri serviceApplicationUri, 
       SPServiceProvisioningContext provisioningContext) {
    if (serviceApplicationProxyType != typeof(CalcServiceApplicationProxy))
      throw new NotSupportedException();

    return new CalcServiceApplicationProxy(name, this, serviceApplicationUri);
  }

  public SPPersistedTypeDescription GetProxyTypeDescription(Type 
       serviceApplicationProxyType) {
    return new SPPersistedTypeDescription("Wingtip Calculator Service Proxy", 
         "Custom service application proxy providing simple 
         calculation capabilities.");
  }

  public Type[] GetProxyTypes() {
    return new Type[] { typeof(CalcServiceApplicationProxy) };
  }
}

Service Application Proxy

The job of the service application proxy is to handle all the communication between the SharePoint WFE and the service application running on one of the back-end application servers. One way to think about this is to envision how a typical application is built to communicate with a web service.

When connecting to a remote web or WCF service, a developer creates a service reference to the remote service in Visual Studio, which creates a proxy within the project to develop against. The service application proxy is similar to this auto-generated web service proxy. However, the service application proxy is much more complicated in that it must interrogate the SharePoint farm for the location of the service application's WCF endpoint.

The service application proxy object can get complicated quite fast because it must create a WCF channel to connect to the service application. First, a WCF host factory must be created, followed by the code to dynamically create a WCF channel and invoke the calls to the service application's WCF endpoint.

Listing 10 shows only a fraction of the code in the CalServiceApplicationProxy.cs because the real proxy is nearly 200 lines long. What has been omitted is the WCF plumbing required to obtain the URI of the WCF endpoint, building the WCF channel and invoking it. What is included are the basic declaration, core properties, and the methods that the service application consumers can call.

Listing 10. Excerpt of the Contents of CalcServiceApplicationProxy.cs

public class CalcServiceApplicationProxy : SPIisWebServiceApplicationProxy {
  private ChannelFactory<IWingtipCalcContract> _channelFactory;
  private object _channelFactoryLock = new object();
  private string _endpointConfigName;

  [Persisted]
  private SPServiceLoadBalancer _loadBalancer;

  public CalcServiceApplicationProxy() { }
  public CalcServiceApplicationProxy(string name, CalcServiceProxy proxy, 
       Uri serviceAddress)
    : base(name, proxy, serviceAddress) {
    // create instance of a new load balancer
    _loadBalancer = new SPRoundRobinServiceLoadBalancer(serviceAddress);
  }

  public override string TypeName {
    get { return "Wingtip Calculator Service Application"; }
  }

  #region service application methods
  public int Add(int x, int y) {
    int result = 0;
    // execute the call against the service app
    ExecuteOnChannel("Add", channel => result = channel.Add(x, y));
    return result;
  }
  public int Subtract(int x, int y) {
    int result = 0;
    // execute the call against the service app
    ExecuteOnChannel("Subtract", channel => result = channel.Subtract(x, y));
    return result;
  }
  #endregion
}

Service Application Proxy Installer

With the service application proxy created, the next step is to install it on the SharePoint WFE servers. Like the service application, this must be done using the SharePoint API because SharePoint 2010 includes no out-of-the-box mechanism for doing this. The best approach is to use the farm-scoped Feature to create the proxy, so add the code shown in Listing 11 to the farm-scoped Feature.

Listing 11. Creating the Proxy

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
  // install the service
  CalcService service = SPFarm.Local.Services.GetValue<CalcService>();
  if (service == null) {
    service = new CalcService(SPFarm.Local);
    service.Update();
  }
  // install the service proxy
  CalcServiceProxy serviceProxy = 
       SPFarm.Local.ServiceProxies.GetValue<CalcServiceProxy>();
  if (serviceProxy == null) {
    serviceProxy = new CalcServiceProxy(SPFarm.Local);
    serviceProxy.Update(true);
  }
  // with service added to the farm, install instance
  CalcServiceInstance serviceInstance = new CalcServiceInstance(SPServer.Local, 
       service);
  serviceInstance.Update(true);
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
  // uninstall the instance
  CalcServiceInstance serviceInstance = 
       SPFarm.Local.Services.GetValue<CalcServiceInstance>();
  if (serviceInstance != null)
    SPServer.Local.ServiceInstances.Remove(serviceInstance.Id);
  // uninstall the service proxy
  CalcServiceProxy serviceProxy = 
       SPFarm.Local.ServiceProxies.GetValue<CalcServiceProxy>();
  if (serviceProxy != null) {
    SPFarm.Local.ServiceProxies.Remove(serviceProxy.Id);
  }
  // uninstall the service
  CalcService service = SPFarm.Local.Services.GetValue<CalcService>();
  if (service != null)
    SPFarm.Local.Services.Remove(service.Id);
}

Once the solution package containing the service application and this Feature is deployed, the Feature will be activated, and administrators should now see an entry in the Connect button on the Manage Service Application page, as shown in Figure 4.

Figure 4. Wingtip service application proxy in Central Administration

Wingtip service application proxy

 

Creating an Instance of the Service Application Proxy

It is now time to create a new instance of the service application proxy. Although this can be done using the Central Administration Web browser interface, that is rather intuitive. This section demonstrates the command-line approach.

First, create the custom Windows PowerShell cmdlet named NewCalcServiceApplicationProxy. The contents of its InternalProcessRecord() is shown in Listing 12. (The rest of the code is omitted for brevity.)

Listing 12: Excerpt of NewCalcServiceApplicationProxy Windows PowerShell Cmdlet

protected override void InternalProcessRecord() {
  // ... validation code omitted ...
    
  Uri serviceApplicationAddress = null;
  if (ParameterSetName == "Uri")
    serviceApplicationAddress = _uri;
  else if (ParameterSetName == "ServiceApplication") {
    // make sure can get a reference to service app
    SPServiceApplication serviceApp = ServiceApplication.Read();
    if (serviceApp == null) {
      WriteError(new InvalidOperationException("Service application not 
           found."), ErrorCategory.ResourceExists, serviceApp);
      SkipProcessCurrentRecord();
    }

    // make sure can connect to service app
    ISharedServiceApplication sharedServiceApp = serviceApp as 
         ISharedServiceApplication;
    if (sharedServiceApp == null) {
      WriteError(new InvalidOperationException("Service application not 
           found."), ErrorCategory.ResourceExists, serviceApp);
      SkipProcessCurrentRecord();
    }

    serviceApplicationAddress = sharedServiceApp.Uri;
  } else
    ThrowTerminatingError(new InvalidOperationException("Invalid parameter 
         set."), ErrorCategory.InvalidArgument, this);

  // create the service app proxy
  if ((serviceApplicationAddress != null) && ShouldProcess(this.Name)) {
    CalcServiceApplicationProxy serviceAppProxy = new CalcServiceApplicationProxy(
        this.Name,
        serviceProxy,
        serviceApplicationAddress);

    // provision the service app proxy
    serviceAppProxy.Provision();

    // pass service app proxy back to the PowerShell
    WriteObject(serviceAppProxy);
  }
}

Register this new cmdlet the same way the previous one was registered, and then run the following script shown in Listing 13 in the SharePoint 2010 Management Shell to create the proxy.

Listing 13. Windows PowerShell Script to Create a New Configuration of the Wingtip Calculator Service Proxy

write-host "Get reference to Wingtip Calculation Service Application" 
     -foregroundcolor Gray
$serviceApp = Get-SPServiceApplication | where { $_.GetType().FullName 
     -eq "CriticalPath.SharePoint.Samples.WingtipCalculator.
     CalcServiceApplication" -and $_.Name 
     -eq "Wingtip Calculation Service Application" }
if ($serviceApp -eq $null){
     Write-Error "CRITICAL ERROR: Failed to acquire reference to Wingtip 
          Calculation Service Application!!!!"
}

write-host "Ensure service application proxy not already created..." -
     foregroundcolor Gray
$serviceAppProxy = Get-SPServiceApplicationProxy | where 
     { $_.GetType().FullName 
     -eq "CriticalPath.SharePoint.Samples.WingtipCalculator.
     CalcServiceApplication" -and $_.Name -eq "Wingtip Calculation 
     Service Application Proxy" }
if ($serviceAppProxy -eq $null)
{
     write-host "Creating service application proxy..." -foregroundcolor Gray
     $serviceAppProxy = New-CalcServiceApplicationProxy -Name "Wingtip 
          Calculation Service Application Proxy" 
          -ServiceApplication $serviceApp
     write-host "Wingtip Calculation Service Application proxy created." 
          -foregroundcolor Green

     write-host 

     write-host "Adding service application proxy to default group..." 
          -foregroundcolor Gray
     Get-SPServiceApplicationProxyGroup -Default | 
          Add-SPServiceApplicationProxyGroupMember 
          -Member $serviceAppProxy
     write-host "Wingtip Calculation Service Application added to default 
          group." -foregroundcolor Green
}

After you run both of these scripts, the Wingtip Calculation Service Application should appear in the list of configured service applications in Central Administration, as shown in Figure 5.

Figure 5. Wingtip Calculation Service Application in Central Administration

Wingtip Calculation Service Application

 

At this point, the service application has been created and deployed, and everything is set up for consumers to be deployed to the SharePoint WFE servers. The next step is to create a consumer.

Creating the Service Consumers

A service application is not terribly useful unless it can be consumed in some shape or form. Consumers can come in various types; for example, web parts, web part pages, or even as custom WCF services to expose the service to applications that are running outside of SharePoint. The consumers could even facilitate a custom administration interface for remote administration of the service.

In the case of the Wingtip Calculator Service, this section demonstrates how to create two different consumers, an Ajax-enabled Web part that can add or subtract two numbers, and a Windows PowerShell cmdlet that can add two numbers together.

Service Consumer Client

Before creating the consumers, one thing developers can do to make the invocation of the service application proxy easier for other developers creating consumers is to provide a service consumer client. This dramatically simplifies the plumbing that is required for consumer developers. Create a new class CalcServiceClient.cs and add the code shown in Listing 14.

Listing 14. Service Application Consumer Client

public sealed class CalcServiceClient {
  private SPServiceContext _serviceContext;

  public CalcServiceClient(SPServiceContext serviceContext) {
    _serviceContext = serviceContext;
  }

  public int Add(int x, int y) {
    int result = 0;
    CalcServiceApplicationProxy.Invoke(
      _serviceContext,
      proxy => result = proxy.Add(x, y)
    );
    return result;
  }

  public int Subtract(int x, int y) {
    int result = 0;
    CalcServiceApplicationProxy.Invoke(
      _serviceContext,
      proxy => result = proxy.Subtract(x, y)
    );
    return result;
  }
}

This will make it much easier to call the service application proxy, because the developers creating service consumers must have to write only a few lines of code.

CalcServiceClient client = new CalcServiceClient(SPServiceContext.Current);
result = client.Add(Int32.Parse(FirstIntTextBox.Text), 
     Int32.Parse(SecondIntTextBox.Text));

Service Consumer Web Part

The first consumer will be based off the SharePoint Developer Tools template Visual Web Part. It will contain two TextBoxes (for the two integers to add or subtract), a DropDownBox (for the add or subtract operand), a Button to execute the operation, and a Label to display the results. All of this is wrapped in an Ajax UpdatePanel to preserve the page postback.

The code-behind for the Visual Web Part's *.ASCX handles the button click event, as shown in Listing 15. Notice how it leverages the client previously created, dramatically simplifying the code required.

Listing 15. Service Application Consumer Visual Web Part Code

public partial class WingtipCalcWebPartUserControl : UserControl {
  protected void ExecuteButton_Click(object sender, EventArgs e) {
    CalcServiceClient client = new CalcServiceClient(SPServiceContext.Current);

    int result = 0;

    switch (OperandDropDownList.SelectedItem.ToString()) {
      case "Add":
        result = client.Add(
            Int32.Parse(FirstIntTextBox.Text),
            Int32.Parse(SecondIntTextBox.Text)
        );
        break;
      case "Subtract":
        result = client.Subtract(
            Int32.Parse(FirstIntTextBox.Text),
            Int32.Parse(SecondIntTextBox.Text)
        );
        break;
    }

    AnswerLabel.Text = result.ToString();
  }
}

Once deployed and added to a page, the resulting Web part would look like Figure 6.

Figure 6. Wingtip service application consumer Web Part

Wingtip service application consumer Web Part

 

Service Consumer PowerShell Cmdlet

Now, create another Wingtip Calculator Service consumer as a Windows PowerShell cmdlet. Add a new class InvokeCalcService.cs in the WingtipCalculatorConsumer project in the PowerShellRegistration folder. This cmdlet will use the client utility class previously created. Add the code shown in Listing 16 to the InvokeCalcService.cs class.

Listing 16. Wingtip Service Application Windows PowerShell Consumer Cmdlet

[Cmdlet("Invoke", "CalcService", SupportsShouldProcess = true)]
public class InvokeCalcService : SPCmdlet {
  private int[] _values;

  [Parameter(Mandatory = true, ValueFromPipeline = true)]
  public SPServiceContextPipeBind ServiceContext;

  [Parameter(ParameterSetName = "Add", Mandatory = true)]
  public int[] Add {
    get { return _values; }
    set { _values = value; }
  }

  protected override void InternalProcessRecord() {
    // get the specified service context
    SPServiceContext serviceContext = ServiceContext.Read();
    if (serviceContext == null)
      WriteError(new InvalidOperationException("Invalid service context."), 
            ErrorCategory.ResourceExists, serviceContext);
    else {
      CalcServiceClient client = new CalcServiceClient(serviceContext);

      // validate only two values were passed in
      if (_values.Length != 2)
        WriteError(new InvalidOperationException("Only two values can be 
             added/subtracted"), ErrorCategory.InvalidArgument, _values);
      else {
        WriteProgress("Executing Calculation", "Sending calculation commands to 
             calculation service application...");
        int result = client.Add(_values[0], _values[1]);
        WriteObject(result);
      }
    }
  }
}

Like the previous Windows PowerShell cmdlets, an XML file is required to register the Invoke-CalcService cmdlet with the SharePoint 2010 Windows PowerShell snap-in.

<ps:Config xmlns:ps="urn:Microsoft.SharePoint.PowerShell"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:schemaLocation="urn:Microsoft.SharePoint.PowerShell 
                SPCmdletSchema.xml">
  <ps:Assembly Name="$SharePoint.Project.AssemblyFullName$">
    <ps:Cmdlet>
      <ps:VerbName>Invoke-CalcService</ps:VerbName>
      <ps:ClassName>CriticalPath.SharePoint.Samples.WingtipCalculator.
           InvokeCalcService</ps:ClassName>
      <ps:HelpFile />
    </ps:Cmdlet>
  </ps:Assembly>
</ps:Config>

To test the custom Windows PowerShell cmdlet service application consumer, open a new SharePoint 2010 Management Shell and run the script shown in Listing 17.

Listing 17. Testing the Wingtip Service Application Windows PowerShell Consumer Cmdlet

$siteUri = 'http://intranet.wingtip.com'
write-host "   Using service context of site $siteUri..." -foregroundcolor Gray

write-host "Adding 1+1..." -foregroundcolor Gray
$result = Invoke-CalcService -ServiceContext $siteUri -Add 1,1
write-host "Result of 1+1 = $result" -foregroundcolor Gray
write-host "Add method on Wingtip Calculation Service Application working." 
     -foregroundcolor Green

It might take a moment for the service application to return the result, but eventually it will return the result of adding the two integers together.

This chapter covered service offerings in the SharePoint platform. After a brief look at the history of service offerings in previous versions of SharePoint, the new SharePoint 2010 service application framework was introduced.

One of the improvements to SharePoint 2010 and the service application framework is the capability for developers to create custom service applications. The majority of this chapter then focused on explaining what is involved in creating a custom service application, and then walked through the process of creating the simple Wingtip Calculator Service application sample.

MVP Andrew Connell is an author, instructor, and co-founder of Critical Path Training, a SharePoint education-focused company. Andrew is a six-time recipient of Microsoft’s Most Valuable Professional (MVP) award (2005-2010) for Microsoft Content Management Server (MCMS) and Microsoft SharePoint Server. He authored and contributed to numerous MCMS and SharePoint books over the years including his book Professional SharePoint 2007 Web Content Management Development by WROX. Andrew speaks about SharePoint development and WCM at conferences such as Tech-Ed, SharePoint Connections, VSLive, SharePoint Best Practice Conference, SharePoint Evolutions Conference, Office Developer Conference, and Microsoft SharePoint Conference in the United States, Australia, England, and Spain. Andrew blogs at Andrew Connell Blog and on Twitter @andrewconnell.

Show:
© 2015 Microsoft