Try fast search NHibernate

01 April 2011

NHibernate 3.2 mapping by code

NHibernate 3.2 will come with its own embedded mapping by code.GalloAlHornoConPapas

If you want know it is not based in fluent-interface, instead it is based on “loquacious”. That said you should understand that it has nothing related with Fluent-NHibernate.

The main idea under the NHibernate’s “sexy mapping” came from my dear ConfORM. In the past year the no conformist red man was running a lot and now I’m ready to transfer most of ConfORM’s intelligence directly inside NHibernate. To continue reading this post you have to run this song.

It’s simple (I’m too sexy)

Simple model

public class MyClass
{
    public int Id { get; set; }
    public string Something { get; set; }
}

Simple mapping

var mapper = new ModelMapper();
mapper.Class<MyClass>(ca =>
{
    ca.Id(x => x.Id, map =>
    {
        map.Column("MyClassId");
        map.Generator(Generators.HighLow, gmap => gmap.Params(new { max_low = 100 }));
    });
    ca.Property(x => x.Something, map => map.Length(150));
});

 

It’s flexible (I’m too sexy for my application)

You can organize your mapping as you want, class-by-class, different concerns about a class in different places and so on… (yes!! if you are a ConfORM user you know the concept)

var mapper = new ModelMapper();
mapper.Class<MyClass>(ca =>
{
    ca.Id(x => x.Id, map =>
    {
        map.Column("MyClassId");
    });
    ca.Id(x => x.Id, map =>
    {
        map.Generator(Generators.HighLow, gmap => gmap.Params(new { max_low = 100 }));
    });
    ca.Property(x => x.Something);
    ca.Property(x => x.Something, map => map.Length(150));
});

As you can see there is no problem duplicating the declaration of a mapped element and you can even do the same with the whole mapping of a class in two different places:

mapper.Class<MyClass>(ca =>
{
    ca.Id(x => x.Id, map =>
    {
        map.Generator(Generators.HighLow, gmap => gmap.Params(new { max_low = 100 }));
    });
    ca.Property(x => x.Something);
});

mapper.Class<MyClass>(ca =>
{
    ca.Id(x => x.Id, map =>
    {
        map.Column("MyClassId");
    });
    ca.Property(x => x.Something, map => map.Length(150));
});

 

It’s conventions friendly (too sexy by far)

You can apply “democratic” conventions

var mapper = new ModelMapper();

mapper.BeforeMapClass +=
    (mi, t, map) => map.Id(x => x.Column((t.Name+"id").ToUpper()));
mapper.BeforeMapProperty +=
    (mi, propertyPath, map) => map.Column(propertyPath.ToColumnName().ToUpper());

mapper.Class<MyClass>(ca =>
{
    ca.Id(x => x.Id, map => { });
    ca.Property(x => x.Something);
});

or you may want “republican” conventions

var mapper = new ModelMapper();

mapper.AfterMapClass +=
    (mi, t, map) => map.Id(x => x.Column((t.Name + "id").ToUpper()));
mapper.AfterMapProperty +=
    (mi, propertyPath, map) => map.Column(propertyPath.ToColumnName().ToUpper());

mapper.Class<MyClass>(ca =>
{
    ca.Id(x => x.Id, map => map.Column("Whatever"));
    ca.Property(x => x.Something, map => map.Column("Whatever"));
});

NHibernate 3.2 too sexy by far!!!

39 comments:

  1. Cool!! And the time to read (and understand) this post is more or less the same that the length of the "too sexy" song. ;-)

    ReplyDelete
  2. Very nice!
    Will it cover the full HBM specification?
    One thing that would be very cool is the ability to map named queries as LINQ functions!
    By the way, haven't seen any reference to the new feature...

    ReplyDelete
  3. @rjperes
    You didn't see references because we are still waiting your patch for the reference manual.
    what you are waiting for ?

    ReplyDelete
  4. Well, at least I'd have to see what it looks like... by references I mean a piece of code...

    ReplyDelete
  5. well... I don't know where you are looking, that code is already in NHibernate sources.

    ReplyDelete
  6. ...just committed, it wasn't there when I looked!

    ReplyDelete
  7. I'm sorry for you but it is not a joke, the code published here are passing tests in NHibernate repository.

    ReplyDelete
  8. I'm curious why you chose to create a new mapping interface instead of using the existing Fluent NHibernate project's interface. I realize fluent nhibernate still generates the XML but it seems like you could have simply adapted their interface to not require the XML such as you have done with your own code mapping

    ReplyDelete
  9. FNH is not so sexy at least for me.
    Now the FNH team can use the embedded API and enjoy it with all its features.

    ReplyDelete
  10. Hi Fabio,
    How much of ConfOrm (btw, I love it, thanks for it!) will the new sexy mapping have? I'm starting a new project and I'm using it as my current mapping of choice, so now I'm wondering now how much refactoring will will be waiting for me by 3.2 release! Oh, and besides, will you continue supporting ConfOrm as it is today?

    ReplyDelete
  11. Thanks for doing such a great job. I really appreciate this style of mapping!

    One thing I would like to know:
    Is is possible to derive mappings, like I can in FNH? Usually I have at least an abstract class "EntityBase" which has some generic properties like Id or ModifiedDate. Do I need to write the mapping code for those again and again for each derived class?

    ReplyDelete
  12. Do you think it is wise to combine xml mappings and this approach.

    I have about 3 years worth of xml mappings. ? Refactoring them would be not cost effective.

    Any advice on a best of both worlds solution

    ReplyDelete
  13. BTW, are automappings on your roadmap, or isn't is sexy enough? ;-)

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
  15. @Paul Cowan if you have investment in xml mapping you might like to try out my mapping approach using LinqToXSD to generate a library for manipulating the xml mappings, which feels a lot like working with the xml mappings see http://dl.dropbox.com/u/2808109/blog/nhmapping/xml%20vs%20linqtoxsd.png for mapping 101. Obviously it can be improved using reflection to build up the strings.

    @rjperes - some automapping goodness in my project too!

    Github link: https://github.com/mcintyre321/NhCodeFirst

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. Hi Fabio, thanks for sharing the code snippets.

    So, is the AfterMap...() set of methods intended to be the way of doing automapping / mapping-by-convention?
    Also, is there a way to do mapping as mapping-class-per-entity not all in one place?

    ReplyDelete
  18. @Harry:
    You mean the posts on adverse conditionals from july 2010? I will look at the code, thanks! And keep on writing... :-)

    ReplyDelete
  19. heh, that was some posts on the concept. The stuff on github is what I've been working on since, so don't let the sell-by date put you off!

    ReplyDelete
  20. Is it possible with the new API to create mapping where an Id is not mapped to any property of a class?

    ReplyDelete
  21. Can it map to Oracle UDT's ?

    ReplyDelete
  22. Hi, this loquacious api for mapping is great. I've begun to use it and is very intuitive. But, I have a problem, I need to map the same class like both Entity and Component, but it throws a MappingException "Ambiguous mapping"

    ReplyDelete
  23. is it possible to specify naming strategies with properties & IDs?

    ReplyDelete
  24. Is this a NH specific thing, or is it also supported in Java's Hibernate? (Though it'shard to think how it would be though considering its lack of lambda)

    ReplyDelete
  25. Can you give example of mapping dynamiccomponent by code?

    ReplyDelete
  26. Here it is
    https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate.Test/MappingByCode/ExpliticMappingTests/DynamicComponentMappingTests.cs

    ReplyDelete
  27. It cost me an hour to understand that parameter should have name "max_lo" not "max_low" :)

    ReplyDelete
  28. Hello
    How can we use stored procedure with that type of mapping ?
    Or can we mix mapping by code and by hbm.xml ?
    Regards

    ReplyDelete
  29. Hi, how do I do a Table per class mapping using mapping by code?

    ReplyDelete
  30. Use this as a reference for all mapping issues: http://knol.google.com/k/nhibernate-chapter-8-inheritance-mapping#8(2E)1(2E)2(2E)(C2)(A0)Table_per_subclass

    Table-per-class can be achieved like this:
    var mapper = new ModelMapper();

    mapper.Class(m =>
    {
    // some mapping goes here (i.e. m.Id)
    });
    mapper.JoinedSubclass(m => {
    m.Table("ChildTable"); //this is optional
    m.Key(k => k.Column("ReferencedId")
    });

    ReplyDelete
  31. hello
    how do you add allow-imports to your class with nhibernate 3.2's mapping by code ?

    ReplyDelete
  32. Hello
    I need to map single class with different tables
    class ClassMapper : ClassMapping
    {
    public ClassMapper()
    {
    Type type = typeof(Tdiff);
    this.EntityName(type.Name);
    Id(x => x.Id);
    Property(x => x.Name);
    Table(type.Name);

    }

    }

    but this class map creates the single mapping or single table ,even i call this mapper many times by passing different types
    any solution?

    ReplyDelete
  33. Hey Fabio - quite a different question. Where does "loquacious interface" term come from? Have you made it up? Or is it used somewhere already and described, like Martin Fowler's on fluent interface? Do you know other APIs based on that kind of interface?

    ReplyDelete
    Replies
    1. @Adam
      Most of implementations of "fluent interface" are not so "fluent"... instead they are more loquacious than XML.

      Delete
    2. That's right. I'm curious have you seen the term "loquacious interface" used somewhere before mapping-by-code?

      Delete
  34. Hey Fabio
    If i add properties dynamically in Persistent class and save .
    as after i want to retrieve those values by Get or Query..
    is it possible? Any way...

    ReplyDelete
  35. This comment has been removed by the author.

    ReplyDelete
  36. Hello! So if my assumption is correct that the mapping is permanent from this point on, how do you undo a mapping (to make it truly "dynamic" :)), say if you had a "main" mapping and you wanted to use a dynamic 1 temporarily/for a particular scenario, then revert to "main"? We have this scenario where a dev inadvertently added an add'l mapping to a subclass who's parent was already mapped, such that when we accessed any(un"discriminator"ed) fields of the parent's mapping that wasn't in the new child's, the new child's mapping superseded/hid those mappings/produced NH "not mapped" exceptions. From that time, devs unaware of the new child mapping would respond to the "not mapped" errors by simply adding (in essence, duplicating) the fields to the new mapping to avoid the error. Did give me an idea tho, that this could come in handy - if I could "undo" it after the specific db interaction - w/out having to touch all the existing mappings to make them dynamic as well (which I guess would also solve the problem). Sorta "just-in-time, discriminator-less" dynamic mappings.

    ReplyDelete