ServerContext and Dependency Injection

I was reviewing some code today and came across a perfect case study for Dependency Injection. It also required another peak under the hood of Microsoft.Office.Server.ServerContext, the results of which I thought might be worth sharing.

From MSDN,  ServerContext “provides run-time methods for shared services in Microsoft Office SharePoint Server 2007.” If you’re not familiar with Shared Service Providers, Shane Young has a concise overview or check out Ted Pattison‘s developer overview on MSDN. The SSP APIs span several namespaces:

  • Microsoft.Office.Excel.Server
  • Microsoft.Office.Server.ApplicationRegistry (Business Data Catalog)
  • Microsoft.Office.Server.Audience
  • Microsoft.Office.Server.Search
  • Microsoft.Office.Server.UserProfiles (includes My Sites)

The entry point to all of these APIs is ultimately a ServerContext object, made available through several static methods and properties:

  • ServerContext.Default
    Returns a new instance for the local farm’s default provider.
  • ServerContext.Current
    Returns ServerContext.GetContext(HttpContext.Current).
  • ServerContext.GetContext(HttpContext httpContext)
    Returns a shared ServerContext instance for the given request context. The provider is determined first through the microsoft.office.server/sharedService section in Web.config, and from the context WebApplication if that fails.
  • ServerContext.GetContext(WebApplication webApplication)
    Returns a new instance for the web application’s provider or the farm’s default provider if one is not specified.
  • ServerContext.GetContext(SPSite site)
    Returns ServerContext.GetContext(site.WebApplication).
  • ServerContext.GetContext(String sharedResourceProviderName)
    Returns a new instance for the named provider if it exists, either in the local farm or its parent.

These details inform some simple usage guidelines:

  1. If you have an HttpContext, use the shared instance from GetContext(HttpContext) or ServerContext.Current.
  2. If you need a specific SSP, use an overload of GetContext().
  3. Else, use ServerContext.Default.

Code Review

The code I was reviewing today was replacing this…

SearchContext.GetContext(SPContext.Current.Site)

…with this…

SearchContext.GetContext(ServerContext.Default)

…to support use of the service in a non-web context. We’re currently working with a single SSP, so Default should be sufficient; however, we can do better. First, the change in question had to be made in several places. For maintainability, it makes more sense to define a field or property to represent the context wherever we need it. Second, by using ServerContext.Default we’re missing out on the benefits of the shared instance provided for an HttpContext, which we usually have.

Dependency Injection

The Dependency Inversion Principle suggests that we should decouple the source of our ServerContext object from how its consumption. What would that look like here? Well the simplest approach is constructor injection:

private ServerContext { get; set; }
public MyService(ServerContext context)
{
    this.ServerContext = context;
}

This is the purest form of DI in that the class knows nothing about where the ServerContext came from, but it’s often practical to “cheat” and expose a nicer interface. In my case, I chose to supplement that constructor with two others:

public MyService() : this(ServerContext.Default) {}

public MyService(HttpContext httpContext)
       : this(ServerContext.GetContext(httpContext) {}

Now my web part can just call MyService(this.Context) and we still have a convenient default constructor for use in PowerShell. Because everything should be usable from PowerShell. This same idea can be applied to a number of common SharePoint tasks, particularly anywhere you’re using SPContext.Current, with benefits including improved reusability, maintainability and testability.

Advertisement