One of the downsides of being confronted with a shared legacy database day in and day out is that you have to map your domain objects to database tables that are also used by other applications. A typical scenario in this case is that those database tables contain more columns than those that are required for your application. These extra columns are specifically there to serve those other legacy applications. Heck, to make matters even worse, there are probably some new columns added specifically for your application as well. This is the fairy tale of shared legacy databases.
Using NHibernate in these scenarios can be challenging sometimes but its built in flexibility and extensibility really helps you to deal with those cases. The problem I ran into last week was that we needed to store a domain object into a table that had a lot more columns than were actually required for our application. If it would be possible to store null values in these columns or if they had default values configured for them in the schema, then this would not be a problem. Instead, these unnecessary columns could not store null values and had no default values associated with them.
First option would be to make some changes to the schema of the table. Alas, no luck there because the other legacy applications that are using the same table would break. Now what?
We needed to insert the default values ourselves, but those columns are not known by NHibernate because they are not mapped to any members of the domain object. One way to solve this, is to pollute the domain object by adding private fields that are initialized to the required default values.
This is probably the simplest option, but imposes a broken window as infrastructure concerns are bleeding into the domain this way. In other words, this is not a viable solution. Keeping the legacy stuff isolated as much as possible, NHibernate provides some ways to deal with this by providing an extensive extensibility model.
After some snooping around in the source code of NHibernate, the solution we chose for dealing with this issue is by creating a custom access strategy. The built in property access strategies are probably already well known, but its also possible to write your own access strategy by implementing the IPropertyAccessor interface.
This simply involves a getter for providing default values and a dummy setter as we're not interested in setting any values on the domain objects. The DefaultValueGetter class uses a trick so that we can keep using the reflection optimizer of NHibernate. This also seems to be necessary when using NHibernate Profiler.
Now we only have to provide some properties in the mapping of the domain object using our custom access strategy:
public class SomeDomainEntity
{
// Legacy fields with no purpose for the domain but required
// by the database.
private Int32 _legacyField1 = 2;
private Boolean _legacyField2 = false;
private String _legacyField3 = "";
}
This is probably not the best solution, but it does the job and prevents polluting the domain objects as a result of database quirks like these. I'm interested in hearing feedback or any better approaches.
Anyway, the easy extensibility of NHibernate makes it the best data access solution around. This way, one can deal with all edge case scenarios that weren't anticipated by the framework builders.
Till next time