Extending NHibernate data with one-to-optional relationships
nhibernate, orm, programming, tech January 3rd, 2010It’s been said that ninety percent of the time you think there is a one-to-one relationship in NHibernate it’s really a many-to-one. I’ll agree with that. But this post is about one of the ten-percent cases when a single entity spans several table schemas.
So the scenario is one where you have a thing that can be extended with additional data. And capabilities, of course, but this post is just about the data. In a traditional app it’s very straightforward to add additional fields when a new feature is added.
Consider a thing with foo and bar fields, before:

And adding the thing with frap and quad fields, after:

This might not be the best way to go in a dynamic system. If the feature was strictly optional some users wouldn’t choose to activate it at all, so adding columns to a core table would be bad. Maybe not all of the things in the system would use the frap-quad fields, so having a very large number of (null) fields could be nice to avoid. In those cases a vertical data partition could be a nice approach.

In fact let’s go the extra step and make a core Thing record, with a ThingType, and move the foo and bar fields onto their own vertical record partition.

Now that’s starting to look a bit crazy, but that’s really the split-up view of a single logical entity. The goal behind breaking them apart is to have Frappable and FooBar aspects mutually ignorant of each other. In the end the logical view of entity #6 is still just the following:

Now let’s wire that together with some data NHibernate classes and Fluent NHibernate.
public class ThingType {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Thing {
public virtual int Id { get; set; }
public virtual ThingType ThingType { get; set; }
}
public class FooBar {
public virtual int Id { get; set; }
public virtual Thing Thing { get; set; }
public virtual string Foo { get; set; }
public virtual string Bar { get; set; }
}
public class Frappable {
public virtual int Id { get; set; }
public virtual Thing Thing { get; set; }
public virtual string Frap { get; set; }
public virtual string Quad { get; set; }
}
As a quick sample let’s use a console app to create and populate a database.
static void Main(string[] args) {
var database = SQLiteConfiguration.Standard.UsingFile("DataSample.db").ShowSql();
var model = AutoMap.Assembly(typeof(Thing).Assembly);
var sessionFactory = Fluently.Configure()
.Database(database)
.Mappings(mappings => mappings.AutoMappings.Add(model))
.ExposeConfiguration(config => new SchemaExport(config).Execute(false, true, false))
.BuildSessionFactory();
Console.WriteLine("Adding records");
var session = sessionFactory.OpenSession();
var fooType = new ThingType { Name = "foo" };
var fooPlusType = new ThingType { Name = "fooplus" };
var thing = new Thing { ThingType = fooPlusType };
var fooBar = new FooBar { Thing = thing, Foo = "brillig", Bar = "slithe" };
var frappable = new Frappable { Thing = thing, Frap = "did", Quad = "and" };
session.Save(fooType);
session.Save(fooPlusType);
session.Save(thing);
session.Save(fooBar);
session.Save(frappable);
session.Close();
Console.WriteLine("Fetching thing #{0}", thing.Id);
session = sessionFactory.OpenSession();
var fetch = session.Get<Thing>(thing.Id);
Console.WriteLine(fetch.ThingType.Name);
session.Close();
}
And the console output is this
Adding records NHibernate: INSERT INTO "ThingType" (Name) VALUES (@p0); select last_insert_rowid();@p0 = 'foo' NHibernate: INSERT INTO "ThingType" (Name) VALUES (@p0); select last_insert_rowid();@p0 = 'fooplus' NHibernate: INSERT INTO "Thing" (ThingType_id) VALUES (@p0); select last_insert_rowid();@p0 = 2 NHibernate: INSERT INTO "FooBar" (Foo, Bar, Thing_id) VALUES (@p0, @p1, @p2); select last_insert_rowid();@p0 = 'brillig', @p1 = 'slithe', @p2 = 1 NHibernate: INSERT INTO "Frappable" (Frap, Quad, Thing_id) VALUES (@p0, @p1, @p2); select last_insert_rowid();@p0 = 'did', @p1 = 'and', @p2 = 1 Fetching thing #1 NHibernate: SELECT thing0_.Id as Id1_0_, thing0_.ThingType_id as ThingType2_1_0_ FROM "Thing" thing0_ WHERE thing0_.Id=@p0;@p0 = 1 NHibernate: SELECT thingtype0_.Id as Id0_0_, thingtype0_.Name as Name0_0_ FROM "ThingType" thingtype0_ WHERE thingtype0_.Id=@p0;@p0 = 2 fooplus Press any key to continue . . .
As expected so far it’s not really a one-to-zero-or-one relationship. The FooBar.Id is an identity column generated by the table instead of copied from Thing.Id, and there really shouldn’t be a FooBar.Thing_id column at all. Let’s update that with an automapping alteration…
class Alteration : IAutoMappingAlteration {
public void Alter(AutoPersistenceModel model) {
model.Override<FooBar>(map => {
map.Id(x => x.Id).GeneratedBy.Foreign("Thing");
map.HasOne(x => x.Thing).Constrained();
});
model.Override<Frappable>(map => {
map.Id(x => x.Id).GeneratedBy.Foreign("Thing");
map.HasOne(x => x.Thing).Constrained();
});
}
}
// and in the main() method
var model = AutoMap.Assembly(typeof(Program).Assembly)
.Alterations(alt => alt.Add(new Alteration()));
And the console output is now:
Adding records NHibernate: INSERT INTO "ThingType" (Name) VALUES (@p0); select last_insert_rowid();@p0 = 'foo' NHibernate: INSERT INTO "ThingType" (Name) VALUES (@p0); select last_insert_rowid();@p0 = 'fooplus' NHibernate: INSERT INTO "Thing" (ThingType_id) VALUES (@p0); select last_insert_rowid();@p0 = 2 NHibernate: INSERT INTO "FooBar" (Foo, Bar, Id) VALUES (@p0, @p1, @p2);@p0 = 'brillig', @p1 = 'slithe', @p2 = 1 NHibernate: INSERT INTO "Frappable" (Frap, Quad, Id) VALUES (@p0, @p1, @p2);@p0 = 'did', @p1 = 'and', @p2 = 1 Fetching thing #1 NHibernate: SELECT thing0_.Id as Id1_0_, thing0_.ThingType_id as ThingType2_1_0_ FROM "Thing" thing0_ WHERE thing0_.Id=@p0;@p0 = 1 NHibernate: SELECT thingtype0_.Id as Id0_0_, thingtype0_.Name as Name0_0_ FROM "ThingType" thingtype0_ WHERE thingtype0_.Id=@p0;@p0 = 2 fooplus Press any key to continue . . .
That’s closer to what we’re looking for. Note the FooBar.Id is an inserted field now where it’s value comes from the Thing property, and the Thing_id column is gone from the schema.
The Id/Thing pair and override become cut-and-paste noise pretty quickly though, so we can factor those into an abstract base class and even make the HasOne alteration work on all data classes descended from it. Like this:
public abstract class ThingPart {
public virtual int Id { get; set; }
public virtual Thing Thing { get; set; }
}
class Alteration : IAutoMappingAlteration {
public void Alter(AutoPersistenceModel model) {
model.OverrideAll(map => {
var recordType = map.GetType().GetGenericArguments().Single();
if (recordType.IsAssignableFrom(typeof(ThingPart))) {
var changeType = typeof(Change<>).MakeGenericType(recordType);
var change = (IChange)Activator.CreateInstance(changeType);
change.Go(map);
}
});
}
interface IChange {
void Go(object mapObject);
}
class Change<TRecord> : IChange where TRecord : ThingPart {
void IChange.Go(object mapObject) {
var map = (AutoMapping<TRecord>)mapObject;
map.Id(x => x.Id).GeneratedBy.Foreign("Thing");
map.HasOne(x => x.Thing).Constrained();
}
}
}
There! In the end the following is all you need for an extension to add a new handful of fields onto thing.
public class FooBar : ThingPart {
public virtual string Foo { get; set; }
public virtual string Bar { get; set; }
}
public class Frappable : ThingPart {
public virtual string Frap { get; set; }
public virtual string Quad { get; set; }
}
There are some more tricks you need to query across several parts at once, but that’s probably another blog post on it’s own.
January 3rd, 2010 at 8:23 pm
I wish I had used NHibernate (and ran across this post) abouot 4 years ago (if the syntax even existed then, minus the 3.5 stuffs) when I worked at a B2B company. I had to do this stuff manually with ADO. Yuck. Good stuff here Lou.
January 4th, 2010 at 11:29 am
Isn’t this the classic case for inheritance? Perhaps the fake domain your showing here makes it less obvious, but that was my first reaction.
You can share primary keys and vertically partition the data.
January 4th, 2010 at 9:44 pm
Nearly, but not quite. There is a similar hub-and-spoke schema with a type discriminator at the hub, but in an inheritance model you wouldn’t have multiple spokes in effect for a given entity.
Fake domain data may have been a mistake in hindsight. Imagine if one related table had fields like url, title, body, and another had something like number of comments, are comments closed, and a has many relationship to comment records. Another could have associated product information. Another still could be a username, email, and hashed password.
In that way you could make a composite thing comprised of the records of various features that have been configured upon it. A feature like being-commentable may also be applied to a thing in general (like a web page, or a user, or a product) even though it wasn’t specifically designed with that specific association at compile time.
January 5th, 2010 at 2:28 am
[...] Extending NHibernate data with one-to-optional relationships - Louis DeJardin takes a look at using NHibernate with entity data which spans across multiple tables [...]
January 5th, 2010 at 9:13 am
Taking your post/comments example then the many-to-any functionality would work this way, and its standard NHibernate config.
Depending on a type identifier column (dealt with by NHibernate) you could have post information, comment information, advertising stats - all as children of the main post object.
Orin Eine dealt with the topic last year: http://ayende.com/Blog/archive/2009/04/22/nhibernate-mapping-ltmany-to-anygt.aspx
January 6th, 2010 at 4:35 am
Ah, interesting! I had no idea that existed. Can that be configured with the fluent api’s?
There are a few things about the particular application I would be concerned about - for example if a modular concern was deactivated or uninstalled would the unknown values in the type identifier cause a problem?
But in any case, yes, that would in most places be an excellent option especially if you wanted NHibernate to handle the persistence entirely transparently.
January 19th, 2010 at 10:37 pm
I asked the FNH group about a fluent api to do an optional relationship (http://groups.google.com/group/fluent-nhibernate/browse_thread/thread/7122ede423d2c297/a27e82564815fa40?hl=en#a27e82564815fa40).
Thanks for this very timely and well written posting!
January 21st, 2010 at 2:30 pm
It turns out that FNH *does* support the NHib any mapping this with ReferencesAny, an undocumented feature. Likely a ReferencesMany also. The test cases are supposed to be good for it too, although I have not looked at them yet.
James Gregory has graciously invited me (and anyone else who can spell fluent nhibernate I’m sure!) to contribute docs…
July 7th, 2010 at 7:30 am
Wouldn’t it be more “correct” to have “Foobar” and “Frappable” *be* (thus; inherit) “Thing” rather than *have* a “Thing”? If you agree, how would your example be mapped with Fluent?