NHibernate’s <any> association is cool

time to read 5 min | 869 words

I need to preface the cool  part with also mentioning that <any> is a part of an evil plot to overtake the world, so don’t use it if you don’t know what you are doing. If you do, however, it is one hell of a feature to have.

Currently I’m using it for a  set of rules, which can be applied to  several objects, which does not have a common ancestor. This is usually a problem, since I have no way to map  those objects to the database and back.

It turned out that NHibernate has support for <any> and <many-to-any> tags which gives you this functionality.  Let us consider the following simple class diagram:

In this case, we have a Rule, which can be applied to an IValidable instance.  How do I persist this to the database? And more importantly, get it out again cleanly?

Let us take a look at the database diagram:

Second, we will look at the mapping:

<class name='NHibernate.Any.Rule, NHibernate.Any' table='`Rule`'>

       <id name='Id'>

              <generator class='identity'/>

       </id>

       <any name='AppliesTo' meta-type='System.String' id-type='System.Int32'>

              <meta-value class='NHibernate.Any.Account, NHibernate.Any' value='ACC'/>

              <meta-value class='NHibernate.Any.Customer, NHibernate.Any' value='CUST'/>

              <column name='AppliesToType'/>

              <column name='AppliesToId'/>

       </any>

</class>

 

A couple of points before we continue:

  • The meta-type attribute is not required in the schema, but omitting it causes some strange results. Always specify it.
  • The <any> tag has the last two elements as <column>. There is a very specific meaning to the ordering of those <column> elements (and it is nearly the only place in NHibernate where element ordering has a meaning). The first <column> element specify the column that holds the type of the association, and the second <column> holds the id of the object that this association points to.
  • Each <meta-value> element maps between a class and a value (which is what NHibernate will search on the Type column).

In this case, both Customer and Account implements IValidable, and a Rule can be applied to either of them (in this simple example, it doesn’t really makes sense, but I have a system where IValidable is a fully fledged interface that exposed enough information to be able to use just its info to run a significant number of rules).

Now I can just set the AppliesTo attribute to the appropriate type, and NHibernate will take care of everything for me. This takes care of quite a bit of complexity, and simplifies my life quite a bit.

Building on this behavior, it is possible to have a very rich model, and still enjoy the benefits of NHibernate’s capabilities.