NHibernate mappings using xml, attributes, and Fluent NHibernate

A few of us at work have been looking at NHibernate 2.0 as a potential ORM tool for our next project. The initial reactions have included (but weren’t limited to) shudders of horror at seeing the mass of angle brackets present in the .hbm.xml mapping files. I don’t think this reaction is atypical – I thought the same thing when I first tried NHibernate. However once I started using it (not for anything serious mind you, just for personal projects. I’m at pre-newbie stage still :)), I found the XML relatively painless. In fact once I’d set them up initially it was great to be able to deal with ordinary POCO (Plain Old C# Object) classes and only think about the persistence mappings when tweaking the DB.

That said, there are alternatives hand coding the .hbm.xml mapping files. I thought I’d cover a few of the options here: NHibernate Mapping Attributes, and Fluent NHibernate.

Getting started

I decided to reuse the extra-simple scenario from my ORM roundup posts. I have a table of suppliers, and a table of states (or provinces, territories, prefectures etc.). Both suppliers and states have names, which are stored as strings/varchars, and IDs, which are stored as Guids/uniqueidentifiers. Each supplier can service many states. So we have a simple many-to-many relationship between the two main entities. It looks a bit like this:

Now let’s create some POCOs for to model our suppliers and states. I’ve added these to an Entities folder in an otherwise blank C# Class Library project.

//State.cs
using System;
namespace Workshop.Entities {    
    public class State {
        public virtual Guid StateId { get; set; }        
        public virtual String Name { get; set; }
    }
}
//Supplier.cs
using System;
using System.Collections.Generic;
namespace Workshop.Entities {
    public class Supplier {
        private IList<State> statesServiced = new List<State>();
        
        public virtual Guid SupplierId { get; set; }
        public virtual String Name { get; set; }
        public virtual IList<State> StatesServiced { get { return statesServiced; } set { statesServiced = value; } }
    }
}

These are nice ordinary classes, with no consideration given as to how they will be persisted. The only concession we have made is making everything virtual by default, so that it is easy for NHibernate to create proxies from these classes.

I’ll also throw in an App.config so NHibernate knows which database to talk to. This time around I’m using NHibernate 2.0, which has a new configuration format from the last time I looked at this example.

Hand coding Hibernate Mapping Files

Time to map our entity classes to our database. Let’s start off with the standard, hand coded .hbm.xml approach. We’ll create a State.hbm.xml file and a Supplier.hbm.xml file to hold our mappings. We’ll also set these files to be compiled in as embedded resources so we can get NHibernate to load the mappings from our DLL (in VS, right click the files in the Solution Explorer and edit the Build Action property).

<!-- State.hbm.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Workshop" namespace="Workshop.Entities">
  <class name="State" table="State">
    <id name="StateId" type="guid">
      <generator class="guid" />
    </id>
    <property name="Name" type="String" />
  </class>
</hibernate-mapping>

<!-- Supplier.hbm.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Workshop" namespace="Workshop.Entities">
  <class name="Supplier" table="Supplier">
    <id name="SupplierId" type="guid">
      <generator class="guid" />
    </id>
    <property name="Name" type="String" />
    <bag name="StatesServiced" table="Supplier_StatesServiced">
      <key column="SupplierId" />
      <many-to-many column="StateId" class="State" />
    </bag>
  </class>
</hibernate-mapping>

Here we are just telling NHibernate how to map each bit of entity data to the database schema. The HBM files include which main table is used to persist each entity, describes each entity’s primary key and standard properties, as well as how the entities relate to each other (in this case we have a many-to-many mapping, using a bag collection). These files are explained in more detail in my Messing around with NHibernate post.

Testing our mappings

I’ve populated my database with some sample data (I actually did it using NHibernate – I find it easier to fill in the relationships that way than with hand written SQL). Let’s create an NHibernateTests.cs file and add a test to make sure the entities are mapped correctly.

public class NHibernateTests {
 protected static readonly ISessionFactory SessionFactory = getSessionFactory();
 
 private static ISessionFactory getSessionFactory() {
  Configuration config = new Configuration();
  config.AddAssembly(Assembly.GetExecutingAssembly());
  return config.BuildSessionFactory();
 }
 
 [Fact]
 public void Get_suppliers_servicing_NSW() {
  using (ISession session = SessionFactory.OpenSession()) {
   var suppliers = session
    .CreateCriteria(typeof(Supplier))
    .CreateCriteria("StatesServiced")
    .Add( Restrictions.Eq("Name", "NSW") )
    .SetProjection( Projections.Property("Name") )
    .List<String>();
   session.Close();
   Assert.NotEmpty(suppliers);
   Assert.Equal(4, suppliers.Count);
   Assert.Contains("Tea Ah Maria", suppliers);
   Assert.Contains("Teas'r'us", suppliers);   
  }
 } 
}

This code intialises a new ISessionFactory using the configuration settings in the App.config, and the config.AddAssembly(Assembly.GetExecutingAssembly()); line adds the .hbm.xml mappings embedded in our DLL.

I’ve also used the Get_suppliers_servicing_NSW() test to run a query against all the mapped entities. I’ll run this test for each configuration change I make to ensure I haven’t broken anything. The details of the test itself aren’t really important, but if you are interested it just asks NHibernate to get suppliers that service the state of NSW, and then uses a projection to only bring back the name of the supplier. The assertions check that the expected number of supplier names are returned, and makes sure the list of names includes a couple of the expected values.

NHibernate Mapping Attributes

NHibernate Mapping Attributes (also know as NHMA) is a part of the NHibernate Contrib project, and can be used to declare mappings via attributes directly on your entities classes. This sort of spoils the whole POCO idea, but has the advantage of allowing you to make changes to the entity and it’s mapping in the one spot. Let’s see how this changes our Supplier and State classes.

//State.cs
[Class]
public class State {
 [Id(0, Name="StateId", TypeType=typeof(Guid), Column="StateId")]
 [Generator(1, Class="guid") ]
 public virtual Guid StateId { get; set; }        

 [Property]
 public virtual String Name { get; set; }
}

//Supplier.cs
[Class]
public class Supplier {
 private IList<State> statesServiced = new List<State>();
 
 [Id(0, Name="SupplierId", TypeType=typeof(Guid))]
 [Generator(1, Class = "guid")]        
 public virtual Guid SupplierId { get; set; }
 
 [Property]
 public virtual String Name { get; set; }
 
 [Bag(0, Table="Supplier_StatesServiced")]
 [Key(1, Column="SupplierId")]
 [ManyToMany(2, Column="StateId", ClassType = typeof(State))]
 public virtual IList<State> StatesServiced { get { return statesServiced; } set { statesServiced = value; } }
} 

We no longer need our .hbm.xml files, and have instead translated the relevant mapping information to attributes on the classes themselves. By using attributes NHMA can infer some of the mapping information we previously had to enter explicitly (for example, <property name="Name" type="String" /> just becomes [Property] public virtual String Name {...}), and we have traded our angle brackets for square ones :). There are also a few strange things going on here. Look at this extract from Supplier.cs:

 [Id(0, Name="SupplierId", TypeType=typeof(Guid))]
 [Generator(1, Class = "guid")]        
 public virtual Guid SupplierId { get; set; }

Notice the element indexes on the attributes (ID is 0, Generator is 1)? This is because NHMA is actually generating your .hbm.xml on the fly from the attributes, and it needs to know the order in which to write out the XML elements (attribute order isn’t maintained using reflection). To further illustrate this attribute to XML translation, let’s look at how this affects our ISessionFactory initialisation for our tests:

//In NHibernateTests.cs
private static ISessionFactory getSessionFactory() {
 var config = new Configuration();            
 HbmSerializer.Default.Validate = true;                        
 HbmSerializer.Default.Serialize(System.Environment.CurrentDirectory, Assembly.GetExecutingAssembly());
 config.AddDirectory(new DirectoryInfo(System.Environment.CurrentDirectory));            
 return config.BuildSessionFactory();
}

This is using the NHMA default HbmSerializer class to generate .hbm.xml files in the current working directory. We then load in these mapping files just like before (except we are using the file system and not embedded assembly resources).

So to my way of thinking all we have really achieved is simply rewritten all our original .hbm.xml logic as attributes rather than XML, and made our previously nice clean POCO entities cluttered with persistence information and subsequently less readable. I really loved the idea of NHMA at first, but after trying it out I can’t really see it providing much benefit over ye olde hand coded XML. As an added deterrent I couldn’t really find much documentation on NHMA, so I ended up having to translate from the fairly well documented .hbm.xml format anyway.

Fluent NHibernate

Fluent NHibernate, like NHMA, offers a non-XML way to configure your mappings. Instead of using attributes though, we are using standard .NET code via a fluent interface. This provides lots of neat advantages like automated refactoring and some compile time support, as well as being able to eliminate duplicated information between .hbm.xml and the POCO entities themselves by using customisable conventions and inferring the relevant property types and names. Before we dive into this it’s worth noting that Fluent NHibernate hasn’t been released yet and so still has some rough edges. I’m using revision 80 from the project’s SVN repository, so much of this will probably change before it hits version 1.0.

I’ve reverted our Supplier and State classes back to Plain Old CLR Objects (removed all the NHMA attributes) and also taken out the .hbm.xml files. The first thing we need to do is create some Fluent Nhibernate ClassMap classes.

public class StateMap : ClassMap<State> {
 public StateMap() {                
  Id(x => x.StateId);
  Map(x => x.Name);
 }
}

public class SupplierMap : ClassMap<Supplier> {
 public SupplierMap() {                
  Id(x => x.SupplierId);
  Map(x => x.Name);
  HasManyToMany<State>(x => x.StatesServiced)
   .AsBag()
   .WithTableName("Supplier_StatesServiced")
   .WithParentKeyColumn("SupplierId")
   .WithChildKeyColumn("StateId");  
 }
}

Each mapping class configures the mapping for an entity within the constructor (um, ignore the evil calls to virtual methods in the constructors :-\). This looks much, much neater than the previously approaches, as Fluent NHibernate does most of the work for us. Let’s check the configuration for our test:

private static ISessionFactory getSessionFactory() {
 var config = new Configuration();
 config.AddMappingsFromAssembly(Assembly.GetExecutingAssembly());
 return config.BuildSessionFactory();
}

Looks pretty good to me. :) But it gets better – say I always name my identifiers in the form EntityNameId. If Fluent NHibernate knew that, then I could reduce my supplier class map to this:

public class SupplierMap : ClassMap<Supplier> {
 public SupplierMap() {                
  Id(x => x.SupplierId);
  Map(x => x.Name);
  HasManyToMany<State>(x => x.StatesServiced)
   .AsBag()
   .WithTableName("Supplier_StatesServiced");
 }
}

After all, we know the keys used to map suppliers and states are going to be SupplierId and StateId, so the many-to-many mapping becomes trivial. Fluent NHibernate currently assumes the names will in the form supplier_id, but that’s no big deal, because it also let’s me change the conventions used:

private static ISessionFactory getSessionFactory() {
 var config = new Configuration();
 var model = getFluentNhibernatePersistenceModel();
 model.Configure(config);            
 return config.BuildSessionFactory();
}

private static PersistenceModel getFluentNhibernatePersistenceModel() {
 var model = new PersistenceModel();
 model.Conventions.GetForeignKeyName = prop => prop.Name + "Id"; //By default assumes prop.Name + "_id"
 model.Conventions.GetForeignKeyNameOfParent = type => type.Name + "Id";
 model.addMappingsFromAssembly(Assembly.GetExecutingAssembly());
 return model;
}

Pretty neat, huh? You can imagine how trivial you could get the mappings if you set your conventions correctly.

There is work under way to make this even easier using AutoMap functionality, which will be used to auto-magically infer mappings for your entities. You can then customise any special cases (like the many-to-many relationship we have in this case – it will probably assume the more common one-to-many case by default).

Tools not covered here

I also had a quick look at ActiveWriter, but couldn’t get it working (failed when trying to save the model and generate the correct files. Possibly because I was using NH 2.0, more likely that I had no idea what I was doing with it :)). It provides a designer within Visual Studio that you can use to generate your mapping files and .NET entity classes. It has the usual drag-and-drop designer goodness – dragging database tables onto the surface or using toolbox items to create classes and adjust the properties to map to tables etc. It is meant to work with both Castle ActiveRecord and NHibernate.

The reason I didn’t persevere with trying to get it to work is that I am well and truly over designers. I hate mousing all over a design surface and repeatedly hunting for the property you want and then repeatedly setting it over multiple boxes. No matter how nice the designer, I always seem to end up cursing it once the demo has finished and I start using it for real work. Of course this may just be a reflection on my advanced age and grumpy demeanor - YMMV. :) Oh yeah, and get off my lawn! :P

There are a number of other tools mentioned in the NHibernate 1.2 docs, including hbm2net (generate classes from HBM), as well as generation tools like CodeSmith and MyGeneration, but most of these seem to focus on generating the C# classes or SQL scripts for creating the database schema from the .hbm.xml. I think I’d prefer to control my entity and database creation, and instead try and make the mapping itself easier.

Conclusion

Fluent NHibernate looks absolutely fantastic, but is still in very early stages of development. If you don’t like living on the bleeding edge, then the standard .hbm.xml files really aren’t that bad once you’ve set up a couple of them. The good thing is you can switch between all these mapping methods without changing anything but the ISessionFactory initialisation code, which only occurs in one place within your application. All these changes were made with a continuously green test light. :)

Comments