An Evening with Fluent NHibernate

So after reading quite a few posts recently about folks dumping their hbm.xml files for fluent nhibernate, I decided to check it out for myself – and in the process formed a couple general thoughts about some other things.

First, a quick look at Fluent NHibernate.  I was initially pretty excited about the prospects of using a C#-based interface for configuring my mappings – my presumption was that by declaring the maps in C# as opposed to XML, I would gain some additional compile-time type checking that I wasn’t currently getting with hbm (at least on one side of the map – I was not expecting for the library to magically go off to my database and validate it against the map (although now that I’m thinking about it, this functionality would make a nice build target or generic unit test)).  As it turns out, I do get some additional type checking – that’s kind of hard to avoid when the map is specified using generics and lambda expressions for returning the mapped members.  Unfortunately, at the end of the day, I still ended up with a MappingException that had absolutely 0 additional context.  After beating my head against the wall for about half an hour, I concluded 2 things: 1) that I had a headache, and 2) that perhaps the mapping classes needed to be public.  I went back and looked at the project sample, and yup, sure enough – I made the classes public and the sample ran just fine.  I came away feeling validated in that the sample worked, but with somewhat of a bad taste in my mouth from having had to take a trial and error approach to figuring out the problem (even though, yea – I should have read looked more closely at the sample code). 

I also found the session factory creation interface to be a bit less comfortable than the equivalent XML configuration settings.  Maybe this is just the grumpy old guy in me talking here, but the following just didn’t feel all that natural.

private static ISessionFactory CreateSessionFactory() {
   var factory = 
      Fluently.Configure()
      .Database(
         MsSqlConfiguration.MsSql2005.ConnectionString(
            d => d
               .Database("Demos.FluentNHibernate.FirstProject")
               .Server("localhost")
               .TrustedConnection())
         .Raw("generate_statistics", "true"))
      .Mappings(
         m => m.FluentMappings.AddFromAssemblyOf<Program>())
      .BuildSessionFactory();
   return factory;
}

While I was following along with the sample, everything was fine, but when I had to go off-course a bit to wire up my application for profiling, there was no very obvious choice, so it was off to Stack Overflow for the solution.

At any rate, my general feeling about using the fluent interface approach for NHibernate mapping specifications is currently that while interesting, the simple change in style is not enough for me to switch over from hbms – and I’m not seeing enough additional gain in functionality or productivity.  That said, I want to immediately make 1 exception for the auto-mapping functionality – that’s just pretty stinkin’ cool.  And while I’m currently a little uncomfortable with simply applying it blindly, I’m sure that after a few weeks of using it in conjunction with NHibernate Profiler, I’ll be feeling pretty good about life without mapping files.

This brings me to my more general thought for the evening.  As I was going through the fluent sample, the bi-directional association piece of it really jumped out at me as smelly.  Specifically, I’m thinking about the following:

public class Store
{
   public Store() {
      Products = new List<Product>();
      Staff = new List<Employee>();
   }

   public virtual int Id { get; private set; }
   public virtual string Name { get; set; }
   public virtual IList<Product> Products { get; set; }
   public virtual IList<Employee> Staff { get; set; }

   public virtual void AddProduct(Product product) {
      product.StoresStockedIn.Add(this);
      Products.Add(product);
   }

   public virtual void AddEmployee(Employee employee) {
      employee.Store = this;
      Staff.Add(employee);
   }
}

public class Product
{
   public Product() {
      StoresStockedIn = new List<Store>();
   }

   public virtual int Id { get; private set; }
   public virtual string Name { get; set; }
   public virtual double Price { get; set; }
   public virtual IList<Store> StoresStockedIn { get; private set; }
}

along with the mapping classes:

public class StoreMap : ClassMap<Store>
{
   public StoreMap() {
      Id(s => s.Id);
      Map(s => s.Name);
      HasMany(s => s.Staff).Inverse();
      HasManyToMany(s => s.Products).Cascade.All().WithTableName(
         "StoreProduct");
   }
}

public class ProductMap : ClassMap<Product>
{
   public ProductMap() {
      Id(x => x.Id);
      Map(x => x.Name);
      Map(x => x.Price);
      HasManyToMany(x => x.StoresStockedIn)
         .Cascade.All()
         .Inverse()
         .WithTableName("StoreProduct");
   }
}

Specifically, I want to focus on the inverse statements and a couple ramifications I see from how this code is put together. First, the domain model is not really 100% persistent-ignorant.  Ok, sure, it is PI in terms of mixing persistence code with domain logic.  However, your model code must be structured in such a way that the behaviors drive people towards interacting with the model in a way that best suits the persistence requirements.  This example model dealt with the persistence concerns of managing products and employees through the Store class by adding helper methods to it for managing the lifetimes of the Product and Employee instances.  However (and secondly), the StoresStockedIn memeber of the Product class is a public property of type IList, which means that it has members for manipulating it’s members.  This actually pushes the lack of semantic PI out of the domain model itself, and would require users of model to know (in this case) that in order to declare that a certain product is sold at a certain store, they need to add the Product instance to the Store via the helper method, rather than adding the Store instance to the Product’s StoresStockedIn collection.  It’s not intuitively obvious, and to me that just feel’s…well…smelly.

I’m going to pull out the section I had written on lazy loading for right now and wrap on the bidirectional associations.  Do you folks see a lot of these in your daily lives and how do you typically go about managing them?  In a non-fluent world, I use the helper methods just like in the example, but then map the association to a private collection member and expose it via IEnumerable.  Is this pretty straightforward to do with Fluent NHibernate?

About Howard Dierking

I like technology...a lot...
This entry was posted in Fluent NHibernate, NHibernate. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Moshe Hoadley

    Hi Guys
    NHibernate now supports JPA (called NPersistence or NPA) including JPA annotations. check it out:
     www.npersistence.com

  • http://codebetter.com/members/james.kovacs/default.aspx james.kovacs

    “I was not expecting for the library to magically go off to my database and validate it against the map”

    Actually FluentNH can almost do that with only a bit of help. Check out PersistenceSpecification. Gabriel Schenker has a nice write-up about it on NHForge:

    http://nhforge.org/blogs/nhibernate/archive/2008/09/02/a-fluent-interface-to-nhibernate-part-1.aspx

    Even better is that PersistenceSpecification does not depend on any way on the rest of FluentNH. You can use it with HBM, NH attributes, or any other NH mapping technique.

  • http://codebetter.com/blogs/howard.dierking/ hdierking

    @James

    That’s fair wrt mixing topics in one post – good thing I held back from writing that section on lazy loading :)

  • http://jagregory.com James Gregory

    @Howard

    Re Stack Overflow: I guess I need to link drop the mailing list more so that’s where you hit first instead of SO :)

    I apologise if my comments came off as a little too ranty, it was early. I guess because you used the exact demo from FNH, it seemed very much like that was a statement against FNH itself, rather than at the symptom that the demo showed. Either way, I think combining your thoughts on FNH and your general thoughts on PI in one post has muddied your point; it’s hard to tell whether the breaking of PI is symptomatic of FNH.

  • hdierking

    @James

    Wrt Stack Overflow – to clarify, I actually started at Google. I don’t see how this pattern of looking for answers is unreasonable or surprising.

    Wrt replacing all NHibernate with Fluent – This is *not* what I was attempting to do, and my commentary here was not intended to be some kind of rant on fluent nhib. Just wanted to try it out using the sandboxed demo.

    Wrt the example and PI – This was the “more general” commentary that I referred to in the opening of the post. It wouldn’t matter whether I was using fluent or not – my point was simply that when you’re dealing with bi-directional associations (and other things that, I would argue, include lazy-loading), there’s semantic bleed of persistence concerns into your model.

  • http://hadihariri.com Hadi Hariri

    @Frans,

    “I find this a little weird… isn’t it so that in general, nhibernate supporters loath the ‘domain model following the database tables’ approach, while to get automapping, you practically have to? ;)”

    In combination schema generation tools from NH, you never need to create the database yourself. In fact, 100% of my project start with my entities and I never create a database manually.

  • Ian Cooper

    Frans, actually DDD says that your model should be ubiquitous i.e. the same in all representations and that the transactional store should reflect the OO model, which should reflect the user’s model of the problem.

    Of course this transactional may be distinct from the model that we use for reporting, hence the idea of a model having a ‘bounded context’

    What NH users want is to build the Db from the OO model, not the other way around.

    I appreciate you are coming from an Object-Role Modelling and not a DDD perspective but there is no contradiction here between DDD and NH

  • http://flux88.com Ben Scheirman

    Personally I think the strong-typed maps make for a compelling argument when doing refactoring. Also, no more mispelling property names in your mapping files!

    As for the collection stuff, I prefer to make the persisted collection private and add a more explicit AddFoo method that takes care of adding to both sides. It conveys the language of the domain more clearly and allows you to manage both lists.

  • http://jagregory.com James Gregory

    Firstly, why all of a sudden is Stack Overflow the first place that anyone goes for support? Since when did it become a better idea to go to a generic programming Q&A site than to the actual product’s mailing list? We have 400+ messages a month, and yet I still have to trawl SO to make sure people get the help they need.

    Our exception handling is pretty poor, and that’s something we need to work on. I’ll be the first to admit this, as it can be a royal pain to figure things out when you map something wrong. Technically, a big problem with exceptions is trying to give more context, NHibernate produces a very detailed exception when anything is incorrect, but it’s all wrapped in one mapping exception type; this makes it difficult for us to figure out what actually went wrong without resorting to pattern matching on the message. It’s on our list of things to do though.

    You can use Fluent however you like. If you don’t like the fluent configuration stuff, don’t use it. It really is as simple as that. I’ve personally never advocated replacing every bit of your NH setup with Fluent, just replace whatever you think has a nicer alternative.

    As for the example, its an example of how to map using Fluent, not how to design a perfectly persistence ignorant application. I never sold it as such, so it shouldn’t be taken as that. Sure you can map to IEnumerables, why didn’t I? Because I’m showing mappings. This design is nothing to do with Fluent, the same style/mistakes can be done with regular HBMs.

    In short: Fluent is still very young, and as such it has some rough edges. We’ve got a great community and if you ask for help in the right place, you shall receive (usually in a few minutes). Don’t use something you don’t like, or are not comfortable with, it just makes everybody unhappy.

    Also, we accept patches.

  • http://www.paulbatum.com Paul Batum

    Frans,

    The automapping functionality does not rely on reading the db schema. If you want you can automap your domain and then use the hbm2ddl tool to generate your schema. So it is certainly NOT the case that the domain model has to follow the database tables.

    Howard,

    I use the same pattern that you’ve outlined when dealing with collections (expose them as IEnumerable). You have several options available as to how you map the private collection member. You can use NHibernate’s traditional approach of mapping the property and then supplying an access modifier (AsCamelCaseField() for example) or if the member is a property (perhaps an autoproperty) it can be mapped using one of the three techniques outlined here:
    http://wiki.fluentnhibernate.org/show/StandardMappingPrivateProperties

  • http://weblogs.asp.net/fbouma Frans Bouma

    “That said, I want to immediately make 1 exception for the auto-mapping functionality – that’s just pretty stinkin’ cool. ”
    I find this a little weird… isn’t it so that in general, nhibernate supporters loath the ‘domain model following the database tables’ approach, while to get automapping, you practically have to? ;)

    About the PI issue: there’s no such thing as persistance ignorance: whatever persistence logic you’re using, it bleeds into your own code one way or the other, be it a base class, be it requirements how to define properties/members, be it how to deal with 2-sided relationships etc.

    Once people understand that, they’ll also understand that o/r mapping isn’t about mapping classes to tables, but really restoring the connection between two projection results: the projection of the entity definition onto code and onto a relational model.

  • http://www.virtew.com Matthieu

    Thanks for sharing your experience.

    I’m using JPA/Hibernate (java equivalent of NHIbernate) since several projects/years and I find that the annotation way is a good compromise between understanding / separation / maintenance od the code
    here’s an example with Toplink : http://www.oracle.com/technology/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html

    I don’t know why in .NET the attributes is not used more ?

    Concerning the association, I go often for a getter wihch returns a unmodifiable collection (http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#unmodifiableList(java.util.List) ) and I’ve public method to add and remove element to the collection (with bi-directionnal setters)

    Matthieu