Ayende @ Rahien

Refunds available at head office

On Fluent NHibernate

There has been some noise lately about Fluent NHibernte. This gives you a fluent interface to configure NHibernate mapping. I don't really see the point, frankly. Fluent NHibernate, at least in its current stage, requires a mapping class per entity. At this point, I might as well use the XML again.

In general, anything that requires me to touch two places to make one change is suspect. That is part of the reason that I like ActiveRecord, the change is highly localized.

NHibernate's XML serve a very different purpose, it is explicitly designed to separate the persistence concern from the entity, so it has to be external. This is annoying, to say the least.

What would be the best of both worlds, however, would be something like this:

public class MyMappingGenerator : MappingGenerator
{
    // another approach would be IEnumerable<Assembly> GetEntitiesAssemblies()
    public override IEnumerable<Type> GetEntityTypes()
    {
        foreach(Type maybeEntity in Assembly.Load("My.Model").GetTypes())
        {
            if(maybeEntity.Namespace == "My.Model.Entities")
                continue;
            yield return maybeEntity;
        }
    }
   
    public override IEnumerable<Type> GetHierarcyRoots()
    {
        yield return typeof(Party);
        yield return typeof(Animal);
    }
   
    public override InheritenceMode GetDefaultInheritenceMode()
    {
        return InheritenceMode.Discriminator;
    }
   
    public override IdentityStrategy GetIdentityStrategy()
    {
        return new HiLo("Id");
    }
   
    public override IEnumerable<MappingStrategy> GetMappingStrategies()
    {
        yield return new MapSimpleProperties();
        yield return new MapManyToOnePropertiesIn(typeof(Party).Assembly);
        yield return new MapManyToManyAssociationsBetween(new Hashtable{
            {typeof(Customer), typeof(Party)},
            {typeof(Party), typeof(Customer)}
        });
        yield return new MapOneToMany().AsSet();
    }
}

This allow you to define the convention, and let the tool deal with it. If it reminds you of Binsor you are correct.

Chad hints that this is either possible to do right now, or will soon be possible, using Fluent NHibernate. And at that point, I would be very interested.

Comments

Jason
08/12/2008 02:27 PM by
Jason

I think this line

if(maybeEntity.Namespace == "My.Model.Entities")

            continue;

should read be negated.

if(maybeEntity.Namespace != "My.Model.Entities")

            continue;

or else your returning every object but the domain.

"Fluent NHibernate, at least in its current stage, requires a mapping class per entity. At this point, I might as well use the XML again."

allows for R# refactorings and compile time checks. You get this with AR, but the argument there is AR "pollutes" the domain. With FI for NH the code could be located in the infrastructure layer, just like the Mapping files are an embedded resource.

James Gregory
08/12/2008 02:41 PM by
James Gregory

This project has only been alive (in my hands) for about 3 weeks. It's still very early days yet, and it concerns me that people are passing judgement long before we've been able to fully implement some of our star features.

Convention support is already available, but it's not complete. We're working on that. We are aiming for eventual full convention based configuration.

Buddy Stein
08/12/2008 02:48 PM by
Buddy Stein

If you don't see the point, then either do I. Next!

Mike
08/12/2008 04:03 PM by
Mike

@James

Don’t get discouraged you are off to a good start. I would like to see ORM Neutral added to the roadmap so you are not locked to NH.

Sean
08/12/2008 04:59 PM by
Sean

I'm very interested in this project and I do see the point. I'm not a fan of AR for nontrivial uses though so that does color my judgement.

I don't really need it to be ORM neutral, I'm happy with it being just for NH.

James Kovacs
08/12/2008 05:34 PM by
James Kovacs

Personally I think that Fluent NHibernate is very exciting and promising. The two big things it brings to NHibernate are better refactoring support and better testability. Refactoring support means that ReSharper can perform property renames. (Currently you get runtime errors from NHibernate because the mapping document and class don't agree. Even without ReSharper, a property rename becomes a compile error rather than a run-time error.) Testability is improved with the PersistenceSpecification class as it gives you a standard way to test your mappings. Yes, convention support would be nice, but right now Fluent NHibernate has some nice building blocks to get there. Overall I really like what I'm seeing!

Sean Kearon
08/12/2008 08:01 PM by
Sean Kearon

I agree with James, I like what I'm seeing, it's extremely exciting and makes NH much more accessible. I'm currently skirting around the edges of deciding to use NH or something else. I want to replace my current ORM approach in a new commercial project I have bubbling. I've spent a good amount of time around NH for the last couple of years and the main thing that has always put me off NH is the XML, period. Loosing the compiler support is not really tenable for me. The fluent interface is very sweet indeed and I think will be widely attractive.

Your approach above is very good too, but less readable than the fluent interface. P.S. I also choose Ninject and Unity over Winsor solely because I can configure them fluently in code...I really can't stand using XML config. I also haven't had time to get into the Boo/Bisnor approach yet - although I have a pdf copy of your book, so things may change!

All in all, I'd be very happy to ditch the XML and buy back the compiler and R# support any day.

Ayende Rahien
08/12/2008 08:08 PM by
Ayende Rahien

sean,

You do realize that you can fully configure Windsor from your code, right?

Jay
08/12/2008 10:51 PM by
Jay

foreach(Type maybeEntity in Assembly.Load("My.Model").GetTypes())

{

if(maybeEntity.Namespace == "My.Model.Entities")

    continue;

yield return maybeEntity;

}

--or--

return Assembly.Load("My.Model").GetTypes().Where(t => t.Namespace != "My.Model.Entities");

Tobin Harris
08/13/2008 05:53 AM by
Tobin Harris

I agree with @Ayende in that what we really want is convention based mapping, which essentially leads to NO mapping (or automatically inferred mapping, to put it another way).

I blogged about achieving this by extending the Mapping.Attributes library, but Fluent NHibernate is in a much better position to get this right IMHO.

Sean Kearon
08/13/2008 06:59 AM by
Sean Kearon

@Ayende, no I didn't and I have no idea how I managed to miss that one. I've just spent five minutes with Windsor and found container.AddComponent<I, T>(). I'm going to get my eyes checked...Thanks :)

Jason
08/13/2008 12:51 PM by
Jason

@Sean

Check out AllTypes.Pick().FromAssembly() this eliminates the need to manually register each object into Windsor.

Comments have been closed on this topic.