Zero-effort mapping for NHibernate?

written by tobinharris on August 8th, 2008 @ 09:50 AM

If you're working with NHibernate, you may realize you have several options for defining your mappings.

Sometimes I don't want to write XML. Neither do I want to use attributes. In fact, I don't even want to do any kind of mapping. This is mainly because:

  • I'm lazy
  • XML is verbose, boring to write, ugly and hard to debug
  • Attributes often represent a separate set of concerns to those of the class they decorate. This makes them a distraction.
  • Mappings often smell of duplication. Much of the time my mappings repeat much of what my classes already say.

Writing mappings is tedious. A computer should be able to do it for me. I'd rather just write code and have my mapper figure out what to do with it. There are exceptions, of course, but most of the time this is how I feel.

I started playing with some code to create inferred mappings. I got a tiny example working.

Essentially, I can write a class like this:

public class Customer
{
    public long Id { get; private set; }
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public List<Order> Orders{ get; set; }
}

Then, my mapper class can infer the NHibernate XML mapping based on conventions such as:

  • Entities always have an Id property which represents their persistent identifier.
  • Lists such as List always get mapped as an NHibernate list.

The beauty of this approach is that 80%-100% of your mappings can be done hands-free, automatically at run-time. You just write domain objects making sure you following some sensible conventions. Of course, the conventions should be customizable so style & rules can be altered to suit the project/team.

In my short spike, I bastardised a copy of the great work already done by Pierre Henri Kuate (et al) in the NHibernate.Mapping.Attributes library.

My idea was that this library should continue to work as usual, but if no mappings attributes are found, it infers them based on sensible defaults. So, you can define zero attributes on your classes and still get mappings for free.

Sadly, I can't take this any further right now. I just wanted to test the basic approach (and dig around some NHContrib code!).

Once I've finished with NHibernate in Action, I may give this project another look. Does it have legs? Can you shoot it down in flames?

If it's a runner, I'd suggest it's done as an extension to NHibernate.Mapping.Attributes. Perhaps called NHibernate.Mapping.Inferred or similar.



kick it on DotNetKicks.com



Comments

  • James Gregory on 08 Aug 15:59

    Hey Tobin, what you’ve outlined here, in terms of inferring defaults, is well within the scope of the Fluent NHibernate project. We’re very much pro convention-over-configuration, and are working on a convention system. We provide defaults that makes assumptions as to how your mappings will be created (ID will always be called ID, for example), and also configurable conventions which allow you to customise the rules for your specific application (for YOUR application, all properties will be accessed through a field, for example).

    The convention support is currently incomplete, as we’re focussing on getting the API solid first. Once we’ve done that, we’re going to focus on fleshing them out.

  • que0x on 08 Aug 17:18

    I prefer ActiveRecord, it’s really easy to start with ! Fluent is hyped at the moment, but i can’t really get the idea

  • Tobin Harris on 08 Aug 19:11

    Hi James

    Great to hear that you’ll be supporting this in Fluent NHibernate :)

    I can see why, given the work you’ve done, you’d be in a good position to support automatic inferred mappings.

    Any idea how it will look?

    For example, will I be able to do:

    var cfg = new NHIbernate.Cfg.Configuration();
    cfg.AddXml( ClassMap.Infer("MyApp.Domain.Model") );

    or

    cfg.AddXml( ClassMap<customer>.Infer() );

    Or have you got something else (cleverer!) lined up?

  • James Gregory on 08 Aug 19:28

    There are about three different ways we’re looking at letting you handle mappings.

    Firstly is the full fluent interface, used to replace XML, nothing more. So it’s as explicit as the mapping files are, but just sans the XML.

    Secondly is using the fluent interface, but with common conventions implied. So you just won’t have to map certain things. Take the below example, with a convention that declares that all Ids are called Id and have an unsaved value of -1, and all lists are bags by default, we could remove the need for the Id setup, and the AsBag call on the HasMany. That’d reduce your mapping down to two lines in this case.

    
    public CustomerMap()
    {
        Id(x => x.Id)
            .WithUnsavedValue(-1);
        Map(x => x.Name);
        HasMany<Product>(x => x.Products)
            .AsBag();
    }
    

    Then there’s the third option, which is a fully implied mapping. We haven’t implemented this yet, but we will soon. It’ll probably be close to your second example. We’ve also considered doing it on an assembly level, but haven’t fleshed that idea out yet.

  • Tobin Harris on 08 Aug 19:29

    @que0x

    ActiveRecord is a brilliant piece of work, I’ve used it several times myself with great success. I’ll use it again for sure.

    The fluent interface is chasing a good ideal too.

    If you don’t like writing XML (I don’t) and you don’t like using attributes (I don’t) then you’ll probably like the fluent interface.

    If they get the inferred mappings working (see this forum post, then you might be freed from any mapping tasks. At least for the simpler models.

  • Jeffery Cote on 12 Nov 12:02

    12kt0994adqvyz56

Post a comment

Options:

Size

Colors