Read the previous posts in this series: Intro, Part 1
In the previous post, we looked at a simple SPIisWebServiceApplication. The SPIisWebServiceApplication is the “instance” of your application that gets provisioned and is managed in Central Administration. It “has” things like the database, jobs and persisted storage associated with it. Soon we’ll add a WCF service which web front end code (and remote farms!) can talk to it with. But before we’ll be able to create the application, we’ll need to define the “Create” links to it in the SharePoint infrastructure. We’ll need a service, a service instance, and then we’ll need a proxy and application proxy in order to talk to it. In this post we’ll focus on these components—they are simple “infrastructure” components without anything super-fancy.
The following class is used to create a menu-item for creating a new Service Application on the Manage Service Applications page. Really the only thing you need to edit is the GetApplicationTypeDescription method and the GetCreateApplicationLink method. The rest is “plumbing” code which you really don’t need to change.
Also note that each of these classes has a Guid attribute. Put a Guid attribute on each “Infrastructure” class for the service application. These generally aren’t referenced, but you’ll need to reference the SPIisWebServiceApplication’s GUID in it’s proxy class.
In the service library project, the following SPIIsWebService class creates the link to creating a new Super Cool Service application. As I mentioned, there’s nothing spectacular about this code. It’s just used to create the link.
using System; using Microsoft.SharePoint.Administration; using System.Runtime.InteropServices; namespace SuperCool.ServiceLibrary { /// <summary> /// The service plumbing-- the thing that's creatable from Central Admin. /// </summary> [Guid("6810AFD9-3137-4bc2-8AF1-00EB8461A861")]// arbitrary GUID, not referenced in code. public sealed class SuperCoolService : SPIisWebService, IServiceAdministration { public SuperCoolService() { } public SuperCoolService(SPFarm farm): base(farm) { } Type[] IServiceAdministration.GetApplicationTypes() { return new Type[] { typeof(SuperCoolServiceApplication) }; } SPPersistedTypeDescription IServiceAdministration.GetApplicationTypeDescription( Type serviceApplicationType) { if (serviceApplicationType != typeof(SuperCoolServiceApplication)) { throw new NotSupportedException(); } return new SPPersistedTypeDescription( "Super Cool Service", "A super cool service application for managing super cool things."); } public override SPAdministrationLink GetCreateApplicationLink(Type serviceApplicationType) { return new SPAdministrationLink("/_admin/SuperCoolServiceApplication/default.aspx"); } public override SPCreateApplicationOptions GetCreateApplicationOptions( Type serviceApplicationType) { return SPCreateApplicationOptions.None; } SPServiceApplication IServiceAdministration.CreateApplication( string name, Type serviceApplicationType, SPServiceProvisioningContext provisioningContext) { throw new NotSupportedException(); } SPServiceApplicationProxy IServiceAdministration.CreateProxy( string name, SPServiceApplication serviceApplication, SPServiceProvisioningContext provisioningContext) { throw new NotSupportedException(); } } }
The next class you’ll need is the SPIisWebServiceInstance class. This isn’t really used for anything but letting SharePoint define which service runs on each server. Nothing spectacular here, and the only thing you really need to specify is the name.
using System.Runtime.InteropServices; using Microsoft.SharePoint.Administration; namespace SuperCool.ServiceLibrary { [Guid("0EBCD546-373C-4c1e-83BD-1D3B504F3E98")] public class SuperCoolServiceInstance : SPIisWebServiceInstance { public SuperCoolServiceInstance() { } public SuperCoolServiceInstance(SPServer server, SuperCoolService service) : base(server, service) { } /// <summary> /// Gets the display name for the administrative UI. /// </summary> public override string TypeName { get { return "Super-Duper Super-Cool Service"; } } } }
That’s it for Service classes. Now we’ll need to add Proxy classes, which are used by the front end to talk to the backend. These are relatively simple classes too, nothing but plumbing.
The SPIisWebServiceProxy is used to connect to the service application. This isn’t the ACTUAL application proxy, just the “glue” to tie the application proxy to the service. It needs bi-directional references with the SPIisWebServiceApplicationProxy, so if you’re writing your own create your own subclasses of SPIisWebServiceProxy and SPIisWebServiceApplicationProxy at this time. My classes will be called SuperCoolServiceProxy and SuperCoolServiceApplicationProxy.
First, the SPIisWebServiceProxy is used to connect the proxies to the service. The important thing here though is that you need to reference the service application’s Guid that this supports. This is the magic that lets it work… without matching Guids in the SupportedServiceApplication attribute it’s not going to work. The rest is self-explanatory, just copy the following code and refactor it for your app.
using System; using Microsoft.SharePoint.Administration; using System.Runtime.InteropServices; namespace SuperCool.ServiceLibrary.Client { [Guid("4DE3E259-EC52-4841-9D5B-B9A3AA095B51")] [SupportedServiceApplication("12A1BE54-04E7-4457-B8FE-6E11647E7EA6", "1.0.0.0", typeof(SuperCoolServiceApplicationProxy))] public class SuperCoolServiceProxy : SPIisWebServiceProxy, IServiceProxyAdministration { public SuperCoolServiceProxy() { } public SuperCoolServiceProxy(SPFarm farm) : base(farm) { } /// <summary> /// Gets an array of the service application proxy types supported by the service proxy. /// </summary> Type[] IServiceProxyAdministration.GetProxyTypes() { return new Type[] { typeof(SuperCoolServiceApplicationProxy) }; } /// <summary> /// Gets a description of the specified service application proxy type. /// </summary> /// <param name="serviceApplicationProxyType">The service application proxy type.</param> /// <returns>A description of the specified type suitable for display.</returns> SPPersistedTypeDescription IServiceProxyAdministration.GetProxyTypeDescription( Type serviceApplicationProxyType) { if (serviceApplicationProxyType != typeof(SuperCoolServiceApplicationProxy)) throw new NotSupportedException(); return new SPPersistedTypeDescription( "Super-Duper Super Cool Service Proxy", "Connects to a Super Cool Service Application for managing super cool things."); } /// <summary> /// Creates an instance of the specified service application proxy type. /// </summary> /// <param name="serviceApplicationProxyType">The type of proxy to create.</param> /// <param name="name">The name of the new proxy.</param> /// <param name="serviceApplicationUri">The address of the connected service application.</param> /// <param name="provisioningContext">Context for the provisioning operation.</param> /// <returns></returns> SPServiceApplicationProxy IServiceProxyAdministration.CreateProxy( Type serviceApplicationProxyType, string name, Uri serviceApplicationUri, SPServiceProvisioningContext provisioningContext) { if (serviceApplicationProxyType != typeof(SuperCoolServiceApplicationProxy)) throw new NotSupportedException(); return new SuperCoolServiceApplicationProxy(name, this, serviceApplicationUri); } } }
Next, the ACTUAL code that is the proxy is responsible for setting up the load balancer and reading the client configuration. You’re going to want to scale your service application across multiple app servers—we need redundancy for our Hello World service app!!! But again, the code is relatively simple. You should be able to refactor the following code without effort. Following is a simple implementation of the SPIisWebServiceApplicationProxy. Next to the SPIisWebServiceApplication class, this is the next most important piece of the puzzle. There isn’t TOO much in here though, we’ll actually code much of the proxy logic into the client.
using System; using Microsoft.SharePoint.Administration; using Microsoft.SharePoint; using System.Configuration; using Microsoft.SharePoint.Utilities; using SuperCool.ServiceLibrary.Services; namespace SuperCool.ServiceLibrary.Client { [System.Runtime.InteropServices.Guid("D49DF84A-77E3-47f9-85D3-965FA3D9E305")] [IisWebServiceApplicationProxyBackupBehavior] public class SuperCoolServiceApplicationProxy : SPIisWebServiceApplicationProxy { [Persisted] private SPServiceLoadBalancer loadBalancer; public SuperCoolServiceApplicationProxy(){} /// <summary> /// Constructs a new service application proxy. /// </summary> /// <param name="name">The name of the new service application proxy.</param> /// <param name="serviceProxy">The parent service proxy.</param> /// <param name="serviceApplicationAddress"> /// The URI of the service application to which the new proxy will connect.</param> public SuperCoolServiceApplicationProxy(string name, SuperCoolServiceProxy serviceProxy, Uri serviceApplicationAddress) : base(name, serviceProxy, serviceApplicationAddress) { // Create a new round-robin load balancer this.loadBalancer = new SPRoundRobinServiceLoadBalancer(serviceApplicationAddress); } /// <summary> /// Gets the display name that describes the object in the admin UI /// </summary> public override string TypeName { get { return "Super Cool Application Proxy"; } } public override void Provision() { this.loadBalancer.Provision(); base.Provision(); } public override void Unprovision(bool deleteData) { // Unprovision the load balancer this.loadBalancer.Unprovision(); base.Unprovision(deleteData); } /// <summary> /// Gets the load balancer used by the service application proxy. /// </summary> internal SPServiceLoadBalancer LoadBalancer { get { return this.loadBalancer; } } /// <summary> /// Gets the configuration used by the service application proxy. /// </summary> internal Configuration Configuration { get { return OpenClientConfiguration(SPUtility.GetGenericSetupPath( @"WebClients\SuperCoolServiceApplication")); } } } }
With those classes, SPIisWebServiceApplication (covered in the last post), SPIisWebService, SPIisWebServiceInstance, SPIisWebServiceProxy and SPIisWebServiceApplicationProxy, we’ve got the SharePoint plumbing part of our service application library completed. Nothing too complex, but a lot of redundant code. In the next post, we’ll create our Hello World WCF service and we’ll cover the WCF plumbing, and then we’ll look at deploying it in the WSP project. After that, we’ll circle back and talk about databases and timer jobs within the service application framework.
About Daniel Larson: I am a SharePoint Architect at NewsGator, (the Social Platform for SharePoint 2010) and author of the best-selling Microsoft Press books Inside Microsoft Windows SharePoint Services 3.0 (with Ted Pattison) and Developing Service-Oriented AJAX Applications.
For the GUID used in the SupportedServiceApplicationProxy “[SupportedServiceApplication("12A1BE54-04E7-4457-B8FE-6E11647E7EA6", "1.0.0.0", typeof(SuperCoolServiceApplicationProxy))]“, is it the GUID used on the SuperCoolServiceApplication class (from the first part of the tutorial) or it is the GUID from the AssemblyInfo.cs in the Properties folder? Thanks.
It is the GUID used on the SuperCoolServiceApplication class (from the first part of the tutorial).
Pingback: Step by Step SharePoint Service Applications : Part 4 (Deploying the Solution) | Daniel Larson's Developer Blog