Home > Uncategorized > Repository Pattern and SharePoint to facade PropertyBag

Repository Pattern and SharePoint to facade PropertyBag

Introduction

Microsoft Patterns and Practices recommend the facading of SharePoint with the repository pattern. If you are an object-oriented programmer that request is straightforward. If your not then it isn’t. There are few examples of this practice and most code samples in SharePoint work directly with the API and SharePoint is scattered throughout the entire code base. If you haven’t read much about this there is a good section in Freeman and Pryce (Growing Object Oriented Software: Guide by Tests) about this approach – they relate this approach back to Cockburn’s ports and adapters and Evans’ Anti-corruption layer. I personally think about it as an anti-corruption layer.

In this example, I will give two examples of how we will avoid SharePoint having too much reach into codebase when using Properties. If we were to not use this solution the case base would be very EASY. Whenever we want a value we would use this code snippet: SPFarm.Local.Properties[key].ToString() (with some Security.RunWithElevatedPrivileges). Using this approach, at best we are likely to see the key as a global constant in some register of keys.

This type of code does not fit the Freeman and Pryce mantra to prefer to write maintainable code over code that is easy to write. Maintainable code has separation of concerns, abstractions and encapsulation – this is also testable code. So in the end in this example, what you’ll see is a lot more code but what’ll you also hopefully appreciate is that we are teasing out domain concepts where SharePoint happens only to the technical implementation.

So, the quick problem domain. We have two simple concepts: a site location and an environment. We have decided that our solution requires both of these pieces of information to be stored in SharePoint. In this case, we have further decided (rightly or wrongly – possibly wrongly) that we are going to let a little bit of SharePoint leak in that both a site location and environment as really property bag values – we make this decision because the current developers think it is easier in the long run. So, we decided against the EASY option.

Easy Option

Create a register:

public class EnvironmentKeys {
  public const string SiteLocationKey = "SiteUrl";
  public const string EnvironmentKey = "Environment";
}

Access it anytime either get:

  var siteUrl = SPFarm.Local.Properties[SiteLocationKey] 

Or update:

  SPFarm.Local.Properties[SiteLocationKey] = "http://newlocation/";
  SPFarm.Local.Update();  // don't worry about privileges as yet

Maintainable option

We are going to create two domain concepts: SiteLocation and Environment both of which are PropertyBagItem and that it will be fronted by a PropertyBagRepository that will allow us to Find or Save. Note: we’ve decided to be a little technology bound because we are using the notion of a property bag when we could just front each domain concept with respository. We can always refactor later – the other agenda here is getting SharePoint devs exposure to writing code using generics.

Here are our domain concepts.

Let’s start with our property bag item contract:

public abstract PropertyBagItem
{
  abstract string Key { get; }
  abstract string Value { get; set; }
}

It has two obvious parts: key and value. Most important here is that we don’t orphan the key from the domain concept. This allows us to avoid the problem of a global register of keys.

And let’s have a new SiteLocation class.

public class SiteLocation : PropertyBagItem
{
  public string Key { get { return "SiteLocationKey"; } }
  public string Value { get; set; }
}

Now, let’s write a test for finding and saving a SiteLocation. This is a pretty ugly test because it requires one being set up. Let’s live with it for this sample.

[TestFixture]

public class PropertyBagItemRepositoryTest
{
  private PropertyBagItemRepository _repos;

  [SetUp]
  public void Setup()
  {
    _repos = new PropertyBagItemRepository();
    _repos.Save(new SiteLocation("http://mysites-test/"));
  }

  [Test]
  public void CanFind()
  {
    Assert.That(_repos.Find<SiteLocation>().Value, Is.EqualTo("http://mysites-test/"));
  }

} 

Now, we’ll look at a possible implementation:

public class PropertyBagItemRepository
{
  private readonly Logger _logger = Logger.Get();

  public T Find<T>() where T : PropertyBagItem, new()
  {
    var property = new T();
    _logger.TraceToDeveloper("PropertyBagItemRepository: Finding key: {0}", property.Key);
    return Security.RunWithElevatedPrivileges(() =>
        {
          if (SPFarm.Local.Properties.ContainsKey(property.Key))
          {
            property.Value = SPFarm.Local.Properties[property.Key].ToString();
            _logger.TraceToDeveloper("PropertyBagItemRepository: Found key with property {0}", property.Value);
          }
          _logger.TraceToDeveloper("PropertyBagItemRepository: Unable to find key: {0}", property.Key);
        return property;
        });
  }
}

That should work and we could then add more tests and an implementation for the Save which might look like – I prefer chaining so I return @T@:

public T Save<T>(T property) where T : PropertyBagItem
{
  _logger.TraceToDeveloper("PropertyBagValueRepository: Save key: {0}", key);
  Security.RunWithElevatedPrivileges(() =>
  {
    SPFarm.Local.Properties[key] = property.Value;
    SPFarm.Local.Update();
  });
  return property;
}

Finally, let’s look at our next domain concept the environment. In this case, we want to enumerate all environments. So, we’ll write our integration test (yes, we should have a unit test for this domain concept first):

[Test]
public void CanFindEnvironment()
{
  Assert.That(new PropertyBagItemRepository().Find<Environment>().Code, Is.EqualTo(Environment.EnvironmentCode.DEVINT));
}

And now we can see that the implementation is a little more complex than the SiteLocation but that we can encapsulate the details well enough – actually, there is some dodgy code but the point is to illustrate that we need to keep environment logic, parsing and checking altogether:

public class Environment : PropertyBagItem
{
  public enum EnvironmentCode { PROD, PREPROD, TEST, DEV }

  public string Key { get { return "EnvironmentKey" } }
  private EnvironmentCode _code;
  public string Value 
  { 
    get { return Enum.GetName(typeof (EnvironmentCode), _code); }
    set { _code = value; }
  }

  
  public Environment(EnvironmentCode code)
  {
    Code = code;
  }

  public Environment(string code)
  {
    Code = Parse(code);
  }
  
  public Environment() : this(EnvironmentCode.DEV) // new() constraint on Find<T> requires parameterless constructor
  {
  }

  public static EnvironmentCode Parse(string property)
  {
    try
    {
      return (EnvironmentCode)Enum.Parse(typeof(EnvironmentCode), property, true);
    }
    catch (Exception)
    { 
      return EnvironmentCode.DEV;
    }
  }
}

It wasn’t that much work really was it?

Categories: Uncategorized Tags: , ,
  1. No comments yet.
  1. No trackbacks yet.