This is the second part of a series on Service Applications. The following has already been posted:
Find the code for this sample on my CodePlex demo portal under the Navigation Service Basic release.
Building a service application
So, let's get started. As expressed in my previous post the demo application is focused on cross-site collection navigation. By will of keyboard there will be summoned a service application called the Global Navigation Service, and each joined web application will be able to use a SiteMapProvider that provides truly global navigation. Of course we'll not begin with WCF proxies or load balancing, not even PowerShell. Let's get cracking with a basic Service Application and build it out over time.
This first sample will have a service and a service application. Since we're in the 'no proxy' mode you provision both the service and the service application using a feature receiver. Next the project contains a simple Web Part to test calling the Service Application.
I'll walk through the code with you from the leaf level to the root, meaning starting with the NavigationApplication, next the NavigationService and finally the event receiver that provisions all of this.
Implementing the NavigationApplication
Although you'll likely code up the NavigationService first since it passes itself into the NavigationApplication, I'll explain how to build the application code first. Have to start somewhere, and there is a two-way dependency between service and application, meaning no good start point anyway.
To create a service application you derive a class of SPServiceApplication, add a GuidAttribute and finally you add at least two constructors, one empty and the other one to form the hierarchy of persisted objects (configuration database objects).
[Guid("f46c5a6a-0f73-4c1e-b11b-c2ffb70c19f9")]
public class NavigationApplication
: SPServiceApplication
{
public NavigationApplication()
{
}
public NavigationApplication(NavigationService service)
: base(String.Empty, service)
{
}
}
That's it. You're done with building the world's most useless application. It only gets to be, not to actually do anything J Just a few more steps to add in some extra functionality. You can override the TypeName property to determine what text to display in the Manage Service Applications page and you should add methods that express the functionality that you are building in your service application. You'll end up with something similar to the following class definition.
[Guid("f46c5a6a-0f73-4c1e-b11b-c2ffb70c19f9")]
public class NavigationApplication : SPServiceApplication
{
public override string TypeName
{
get { return "Navigation Application (Demo)"; }
}
public NavigationApplication()
{
}
public NavigationApplication(NavigationService service)
: base(String.Empty, service)
{
}
internal static NavigationApplication Create(NavigationService service)
{
return new NavigationApplication(service);
}
public string GetNavigation()
{
return "Demo";
}
}
That's on the Service Application side. Next up is the farm wide service that manages this application.
Implementing the NavigationService
The service application will be managed by the farm-wide service class dubbed NavigationService. During provisioning the NavigationService will create the NavigationApplication. It isn't doing anything else but this. The 'singleton' application is exposed as a property so that code can call the application via the service.
[Guid("50c2366b-24a5-4b9a-ad47-e6d6177c630c")]
public class NavigationService : SPService
{
public NavigationApplication Application
{
get { return Applications.Cast<NavigationApplication>().First(); }
}
public NavigationService()
{
}
public NavigationService(SPFarm farm)
: base(String.Empty, farm)
{
}
public override void Provision()
{
base.Provision();
NavigationApplication application =
GetChild<NavigationApplication>(String.Empty);
if (application == null)
{
application = NavigationApplication.Create(this);
application.Update();
application.Provision();
}
}
}
Provisioning the service
To get the service application going you need to instantiate the service one time within the farm. One vehicle to achieve this is a feature receiver bound to a farm-level feature. There are some things to consider namely that the Manage Service Applications page also has a delete button to remove applications. Since the feature receiver is only called once you cannot use this approach to recreate your Service Application.
public override void FeatureActivated(
SPFeatureReceiverProperties properties)
{
SPWebService adminService =
(SPWebService)properties.Feature.Parent;
SPFarm farm = adminService.Farm;
NavigationService service =
farm.Services.GetValue<NavigationService>();
if (service == null)
{
service = new NavigationService(farm);
service.Update();
service.Provision();
}
}
Now that you have both a running service which houses a Service Application it is time to test this application using a Web Part.
Calling the Service Application
The last step is also the easiest. You of course need a consumer for the service application, and a Web Part is one easy vehicle to use. To call the Service Application you first retrieve the service. Via the service you can grab the service application and call methods on it.
public class NavigationWebPart : WebPart
{
protected override void Render(HtmlTextWriter writer)
{
NavigationService service = SPFarm.Local.GetChild<NavigationService>();
writer.WriteEncodedText(service.Application.GetNavigation());
}
}
That's it for today. This concludes the demo on building a very simple service application. You can now start tacking on extra stuff. Check out the SPServiceApplication base class to see what is available.
Hope it helps!