Valio.fi deep dive #8: Resources and ORM

Now we’re standing at the edge of the code pool. Let’s dive in! Good background reading: Deep dive #4 on ORM choice, deep dive #7 on database schema.

imageThe resource model

As I described in my previous post, we ended up storing all different types of resources in one table. However, we model each of them as a separate class (a subclass of Resource). Even though resources themselves don’t have too many fields to separate them from one another, we have lots of code specific for each resource type. Thus, strong typing of resources improves intellisense experience and type safety. Mapping-wise, we use the Type column as the discriminator.

When the project was born, NHibernate 3 wasn’t out yet, so we used NH 2.x + FluentNHibernate – thus, the following mapping examples are in the fluent syntax instead of the canonical XML.

Before we look at that, let’s recap what we’re trying to achieve:

We have a “data” column of type xml. That XML contains two distinct elements of data (simplified but sufficient truth): First, the widgets structure, which is really common for all types of resources, and second, the fields specific to each resource type.

This duality causes us some headache. When mapping an XML column in NHibernate, you have to map it as a custom type. A user type mapper does not support splitting a table column into several properties. It does allow us to serialize and deserialize a complex object, though.

Getting the containment right

Since our data column contained structure specific to a resource type, we had to map the data xml column in Resource’s subclasses. Enabling the widget data (common to all Resources) to be accessed on the Resource base class level only required a little class design.

Our Resource class contains a property of type ResourceData which contains an IList<WidgetConfiguration>. ResourceData is in fact abstract: each of the Resource’s subtypes also defines a ResourceData derivative that matches its own set of custom fields. For example, a RecipePage.CustomData class adds a RecipeId field.

At the other end, the subclasses of Resource introduce fields driven by their custom data. For example, RecipePage exposes:

public virtual Guid RecipeId { 
  get { return ((CustomData)CustomResourceData).RecipeId; } 
  set { ((CustomData)CustomResourceData).RecipeId = value; } 
}

The typecast is somewhat ugly and expects that all Resource.CustomResourceData instances must be in sync with the equivalent Resource subtypes (a RecipePage always expect a RecipePage.CustomData). Of course, this isn’t much of an issue in practice.

We could have added some strong typing by making Resource a public class Resource<TCustomData> where TCustomData : ResourceData, and then have a “public virtual TCustomData CustomResourceData” on it. That way, a RecipePage would have inherited from Resource<RecipePage.CustomData> and thus gained a strongly-typed handle on its custom data.

The main reason we didn’t do this was because it would have required us to introduce an IResource or a ResourceBase for all those scenarios where we didn’t care about the resource type – the vast majority of all resource-based code. In retrospect, this would probably be a good idea. It was just too risky to implement at that stage of the project. Still, it’s no more than a small blemish – the typecasts really don’t affect your everyday programming at all, and modifying the existing resource types is pretty uncommon.

Subclass mapping and other fun

To load the resources this way, we then had to implement quite a few NHibernate mappings. We had a mapping for each of the resource type classes:

public class ProductPageMap : ResourceSubclassMap<ProductPage, ProductPage.CustomData>
{
    public ProductPageMap() : base("Product") {}
}

The parts that had to be changed in each of the mappings are in bold. In short, we had the mapping name (not interpreted, just a convention), the resource class type argument and the custom data type argument. To support such a (relatively) terse declaration, we had implemented the ResourceSubclassMap helper:

public class ResourceSubclassMap<TResource, TCustomDataType> : SubclassMap<TResource>
	where TResource : Resource
	where TCustomDataType : ResourceData, IEquatable<TCustomDataType>, new()
{
	public ResourceSubclassMap(string discriminatorValue)
	{
		Extends(typeof(Resource));
		DiscriminatorValue(discriminatorValue);
		Map(r => r.CustomResourceData).Column("data").CustomType(typeof(ResourceDataMapper<TCustomDataType>));
	}
}

To sum it up, wiring up mappings of this kind was actually very simple. Also, due to the nice extensibility of FluentNHibernate's mapping syntax, we could even easily throw in some calculated fields. For example, since we commonly accessed the RecipeId property of the RecipePage class (a member stored in the data xml for that resource type), we declared a public Guid RecipeId and specified a mapping like this:

public class RecipePageMap : ResourceSubclassMap<RecipePage, RecipePage.CustomData>
{
	public RecipePageMap() : base("Recipe")
	{
		Map(ap => ap.RecipeId)
                  .Formula("cast(data.query('/data/recipeId/node()') as nvarchar(max))")
                  .ReadOnly();
	}
}

Why bother, you ask? Since our RecipePage objects have a strongly typed notion of RecipePage.CustomData, why not just access that?

This is one of the places where we can actually deliver some performance improvements by tweaking the query model. Defining the formula of the XML access – the data.query syntax is XQuery that gets executed inside SQL Server – enables us to run NHibernate HQL queries on that particular XML data fragment. A condition of “RecipeId = foo” gets translated into SQL, and SQL Server does a good job of optimizing the XML field queries.

Without this trick, we would have to load all the RecipePages into memory and filter the list there; of course, for most scenarios they’re all cached anyway. Still, using the formula enabled us to skip caching on scenarios that would have otherwise been far too slow and still required real-time data with no cache impact surprises.

ResourceDataMapper<T>, then?

And finally, it all winds down to ResourceDataMapper<T>, referenced in the ResourceSubclassMap constructor. How does that construct the data objects? Well, that’s easy, because we simplified quite a few things. ResourceSubclassMap is just an NHibernate IUserType implementation, essentially meaning that it has a converter from the database format (in this case, xml) to the complex user type (in this case, the CustomData type). Skipping some boilerplate, here’s the meat:

public class ResourceDataMapper<T> : XDocumentCompositeField<T>
	where T : ResourceData, IEquatable<T>, new()
{
	public override T XmlToComposite(XDocument data)
	{
		var result = new T {
			RedirectUrl = data.XPathSelectElement("/data/redirectUrl").NullSafeSelect(e=>e.Value),
			AvailableWidgetZones = WidgetConfiguration.GetZones(data.XPathSelectElement("/data/zones")),
			WidgetConfigurations = WidgetConfiguration.ParseWidgetConfigurations(data.XPathSelectElement("/data/zones"))
		};
		result.ParseXml(data.XPathSelectElement("data"));
		return result;
	}

The most important things here are:

  1. ResourceDataMapper takes a type argument – the custom data type – and requires that it’s construable (the new() constraint) and that it inherits from ResourceData. It also requires IEquatable<T>, but that’s just to satisfy the C# compiler’s insistence to make sure we’re following the IUserType contract.
  2. Those assumptions are then relied upon: a new T – for example, a RecipePage.CustomData – is constructed. Next, its common fields, i.e. the ones in every ResourceData object such as the widget list and the redirect url, get populated. Finally, an abstract method called ParseXml is then called.
  3. And no surprise here, each of the CustomData implementations then have a ParseXml implementation of their own. An example follows.
internal override void ParseXml(System.Xml.Linq.XElement xmlSource)
{
	var configuredRecipeId = xmlSource.Descendants("recipeId").FirstOrDefault().NullSafeSelect(n => n.Value.TryParseGuid());

	if (configuredRecipeId.HasValue)
	{
		RecipeId = configuredRecipeId.Value;
	}
}

We also have all of this backwards, i.e. each CustomData has a SerializeToXml() method that allows us to save the changes in the objects. A ResourceDataMapper then calls this method and NHibernate gets its precious XML to be stored in the database. Not particularly complicated once you have it written up!

Widgets come next

I already made a passing mention to widgets getting parsed from the XML above. Yep, they do get pulled out by the ResourceDataMapper. But after that, they are brought to life and rendered. In the next episode, I’ll cover the widgetry. It’ll be just another example of the same paradigm we’re using here, but with a bit more actual logic (rendering, editors and whatnot). Until next time!

January 10, 2012 · Jouni Heikniemi · One Comment
Tags: , ,  · Posted in: .NET, Web

One Response

  1. Mika Kolari - January 21, 2012

    I've used Fluent NHibernate and various kinds of subclass mapping strategies (table-per-hierarchy, table-per-subclass, joined-subclass), but I've never needed that Extends-method in SubclassMap. What does it actually do?

Leave a Reply