Wednesday, February 15, 2012

NHibernate's mapping-by-code - the summary

Six weeks ago, when I started my experiments with NHibernate's 3.2 new mapping feature - mapping-by-code, I was a loyal Fluent NHibernate user and a fan of method chains in APIs. My first impression about mapping-by-code was that it seems to be a good direction, but it's still immature and - what's important - not documented at all. I decided to have a deeper look and it turned into almost twenty parts series exploring all the possible mappings - probably the only complete guide to mapping-by-code on the web so far. Time to sum the series up.

Let's start with what mapping-by-code is. It is an XML-less mapping solution being an integral part of NHibernate since 3.2, based on ConfORM library. Its API tries to conform to XML naming and structure. There's a strong convention in how the mapping methods are built. Its names are almost always equal to XML elements names. The first parameter points to the mapped property, second is for its options corresponding XML attributes (and XML <key> element, if applicable) and the rest of parameters, if any, corresponds to nested XML elements. It's very convenient for those familiar with XML schema or for documentation readers.

Mapping-by-code also came with very powerful mapping by convention tool - ConventionModelMapper. It is highly flexible and customizable, but customizing it may not even be needed, as by default it is able to figure out mappings even for components or maps. The only thing it can't map automatically are bidirectional relationships - but it was pretty easy to fix this using conventions (I've updated my conventions since first published - it now supports all kinds of collections, inheritance and more - feel free to use it).

Here is the full table of contents of my mapping-by-code series.

  1. First impressions
  2. Naming convention resembling Fluent
  3. Property
  4. Component
  5. ManyToOne
  6. inheritance
  7. dynamic component
  8. Set and Bag
  9. OneToMany and other collection-based relation types
  10. concurrency
  11. OneToOne
  12. Join
  13. Any
  14. List, Array, IdBag
  15. Map
  16. Id, NaturalId
  17. composite identifiers
  18. entity-level mappings

And what about Fluent NHibernate? Hiding the XML was a great idea, but simplifying the mappings went too far, in my opinion. I've already mentioned the mess caused by concept name changes made in Fluent NHibernate (1) (2) - I wouldn't repeat it again. Moreover, XML mapping is a tree structure and it just doesn't fit into single method chains. Fluent NHibernate's API bypasses this limitations by prefixing method names (like KeyColumn) or by falling back to the interface that uses Action<T> (i.e. in Join or Component mapping), quite similiar to mapping-by-code API. Method chaining also makes it hard to reuse the same concepts in different contexts. It's lot easier in mapping-by-code way - i.e. Column mapping is the same in every mapped feature and it is handled by exactly the same code.

Don't get me wrong. I think FNH was a good and useful project. But I've used it as the only existing alternative to cumbersome and verbose XML mapping. And now, when we have an alternative that is integrated into NHibernate (no external dependency and versioning issues), more efficient (no XML serialization) and with better API (no ambiguity, NH naming kept), the purpose of FNH's existence is highly reduced.

17 comments:

  1. Thanks for your posts: this is so far really the only complete guide on mapping by code.

    ReplyDelete
  2. What about Fluent NHibernate Automapping?

    It's the main reason I use FNH, and I don't see anything remotely equivalent in NH MbC.

    -Tom Bushell

    ReplyDelete
  3. Tom, have a look at mapping by conventions - see Fabio Maulo's post

    ReplyDelete
    Replies
    1. I'm familiar with that post. Fabio goes out of his way to say "this is not Automapping".

      FNH Automapping is much more advanced. You give it an object model, it creates a database schema, with no further help needed from the programmer.

      It's (mostly), just that simple.

      -Tom Bushell

      Delete
    2. I've used automapping in FNH to generate my schema many times, I'm also using mapping-by-code for that purpose with success. As I've mentioned in the article, MBC ConventionalModelMapper (with few custom conventions for bidirectional relationships) does the trick for me. I give it an object model, it creates a database schema, with no more help than in FNH's case. I find it even better than FNH's as FNH can't automap maps or components so easily. What else does FNH automapping offer?

      The discussion about whether it is an automapping or not is just a nomenclature issue for me.

      Delete
  4. Link 11 ain't correct, should be http://notherdev.blogspot.com/2012/01/mapping-by-code-onetoone.html

    ReplyDelete
  5. thanks a lot! i think these post are very useful for every one who want to live in NHibernate zone, thanks again.

    ReplyDelete
  6. Adam, would you be interested in writing a short book on NHibernate? If so, contact me at jonathan@infoq.com.

    Jonathan Allen
    Lead Editor
    InfoQ

    ReplyDelete
  7. Hi,

    I have an NHibernate mapping problem described on the following link:
    http://stackoverflow.com/questions/10266347/nhibernate-composite-id-mapping-issue-bi-uni-directional-onetomany-manytoone-a

    It's occuring very headache for me, somebody cal help me please?

    Thank you,
    Zoltan

    ReplyDelete
  8. The collection of articles are awesome. Is code sample available for download?

    ReplyDelete
    Replies
    1. Thanks. Well, no, there is no real code available in the articles, it's always more like API listing. I can't see how this could be valuable as a standalone code samble.

      Delete
  9. Thank you, the guide is a big help to me.

    ReplyDelete
  10. Hi, I am trying to use an IUserType with the ID field in mapping-by-code, but I'm stuck. I have got it working with a normal property, just can't seem to get anywhere with the ID field. Any tips? Thanks

    ReplyDelete
  11. Wow...just stumbled upon this recently. Thank you!

    ReplyDelete
  12. Hi,

    Just start trying the Mapping-by-code feature of NHibernate. Your blog is by far the most helpful one. Everything works as you explained except this one:

    I have a component call ContactInfo which is used by many classes. There is a property called "Email" which is not needed in every class using the component. For example, if used as an account Billing Contact, Email will be used. If used in a bill as Payer, Email is not tracked. However, if I map Email in the case of BillingContact, when loading Bill objects, I get

    Invalid column name 'Email'.

    I can see the SQL generated as "SELECT ... bills0_.Email, ...".

    It looks to me that once a component is mapped, the mapping logic is reused and all mapped properties are assumed mapped in any classes using the component. Is this the convention? If so, is there a way to override it?

    Thank you for your help!

    ReplyDelete