views:

109

answers:

2

First time rocking it with NHibernate/Fluent so apologies in advance if this is a naive question. I have a set of Models I want to map. When I create my session factory I'm trying to do all mappings at once. I am not using auto-mapping (though I may if what I am trying to do ends up being more painful than it ought to be). The problem I am running into is that it seems only the top map is taking. Given the code snippet below and running a unit test that attempts to save 'bar', it fails and checking the logs I see NHibernate is trying to save a bar entity to the foo table. While I suspect it's my mappings it could be something else that I am simply overlooking.

Code that creates the session factory (note I've also tried separate calls into .Mappings):

Fluently.Configure().Database(MsSqlConfiguration.MsSql2008
    .ConnectionString(c => c
        .Server(@"localhost\SQLEXPRESS")
        .Database("foo")
        .Username("foo")
        .Password("foo")))
    .Mappings(m => 
        { 
            m.FluentMappings.AddFromAssemblyOf<FooMap>()
                .Conventions.Add(FluentNHibernate.Conventions.Helpers
                .Table.Is(x => "foos"));
            m.FluentMappings.AddFromAssemblyOf<BarMap>()
                .Conventions.Add(FluentNHibernate.Conventions.Helpers
                .Table.Is(x => "bars"));
        })
    .BuildSessionFactory();

Unit test snippet:

using (var session = Data.SessionHelper.SessionFactory.OpenSession()) {      
   var bar = new Bar();
   session.Save(bar);
   Assert.NotNull(bar.Id);
}
A: 

With classmap you specify the table name in the mapping. If not specified, it will be the same as the entity class name.

class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Table("foos");
    }
}

Conventions apply to all mappings. As you added 2 table name conventions, only 1 will take effect.

Are your FooMap and BarMap in the same assembly? You only need to add each assembly once.

.Mappings(m => m.FluentMappings.AddFromAssemblyOf<FooMap>())
Lachlan Roche
This helps too! Between both answers I think I'm good to go.
chris.baglieri
+1  A: 

You're doing it wrong. :)

Firstly, m.FluentMappings.AddFromAssemblyOf<FooMap>() and m.FluentMappings.AddFromAssemblyOf<BarMap>() are doing exactly the same thing (if FooMap and BarMap are in the same assembly). Each one just tells Fluent NHibernate to scan the assembly that contains the generic type; so if both types are in the same assembly, it'll scan it twice.

Secondly, the Conventions call is not scoped to the specific assembly you call it after, it's for the whole set of mappings. So what you're doing is supplying two conventions to set the table name to an explicit value, and the second one is the last one to be applied. What you want to do is use the x parameter (which is the entity type) and create your table name from that.

What you need is something like this:

.Mappings(m => 
{ 
  m.FluentMappings.AddFromAssemblyOf<FooMap>()
   .Conventions.Add(Table.Is(x => x.Name + "s"));
})

Obviously my implementation is naive, and depending on what your table naming convention is you might want to use a pluraliser (I believe Castle has one, but it shouldn't be hard to find one with google).

You can read up more about conventions on the Fluent NHibernate wiki.

James Gregory
I figured as much, thanks, this helps.
chris.baglieri