NHibernate’s <any> association is cool
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.
Comments
Comment preview