views:

42

answers:

2

I'm using FluentNHibernate. I am not using auto-mapping. I have a base class that is subclassed. When I query against the base class then it performs an extra query against the subclass. Here's the (contrived) example of what I'm doing:

public class Foo
{
    int Id;
    string SomeValue;
}

I create another class that represents an audit record of the first and I inherit it:

public class FooAudit : Foo
{
    DateTime DateModified;
}

I create separate mappings for each that go to their own tables:

public class FooMap : ClassMap<Foo>
{
    public FooAuditMap()
    {
        Table("Foo");
        Id(x => x.Id).Column("FOO_ID");
        Map(x => x.SomeValue).Column("SOME_VALUE");
    }
}

public class FooAuditMap : ClassMap<FooAuditMap>
{
    public FooAuditMap()
    {
        Table("FooAudit");
        CompositeId()
            .KeyProperty(x => x.DateModified, c => c.ColumnName("AUDIT_DATE"));
            .KeyProperty(x => x.Id, c => c.ColumnName("FOO_ID"));
        Map(x => x.SomeValue).Column("SOME_VALUE");
    }
}

I perform a query against Foo:

public virtual IEnumerable<Foo> List()
{
    using (var session = SessionFactory.OpenSession())
    {
        return session.CreateCriteria<Foo>().List<Foo>();
    }
}

Which then hits the DB twice, once to execute that query against Foo and again to FooAudit.

Why is it making two queries? I generated the HBM files and there is absolutely nothing linking these classes.

EDIT: For completeness, this is what the bootstrap config looks like.

public static ISessionFactory CreateSessionFactory()
{
    return Fluently
        .Configure()
        .Database
        (
            FluentNHibernate.Cfg
            .Db.MsSqlConfiguration.MsSql2005
            .ConnectionString(GetConnectionString())
        )
        .Mappings(m => m
            .FluentMappings.AddFromAssemblyOf<Foo>()
            .Conventions.Add(typeof(EnumConvention)))
        .BuildSessionFactory();
}
A: 

Try:

public class FooAuditMap : SubclassMap<FooAudit>
{
    public FooAuditMap()
    {
        Table("FooAudit");
        CompositeId()
            .KeyProperty(x => x.DateModified, c => c.ColumnName("AUDIT_DATE"));
            .KeyProperty(x => x.Id, c => c.ColumnName("FOO_ID"));
        Map(x => x.SomeValue).Column("SOME_VALUE");
    }
}

You still have a problem because the identifier for Foo and FooAudit are different. If FooAudit is a subclass of Foo it should have the same identifier stored in the Foo table.

Update based on OP comment: You can certainly have an inheritance chain in the domain model without expressing it in NHibnerate. Just change the FooAuditMap to inherit ClassMap<FooAudit>. However your query for object of type Foo will not include any FooAudit types because NH doesn't know about the relationship.

But I think what you have here is a one-to-many relationship -- Foo has a collection of FooAudits -- and you should map it that way.

Update 2: It appears that my previous statement "However your query for object of type Foo will not include any FooAudit types because NH doesn't know about the relationship." is wrong. There is a way to return just the base class by restricting the results using the special class property but that's not a great solution.

I think you would be better off to get rid of the inheritance and either have both classes implement an interface or map the common properties as a component.

Jamie Ide
I may be doing something that's unnatural to NHibernate. Think of Foo and FooAudit as two entirely separate things. One just happens to derive from the other to reuse some of the properties. I want to take advantage of the reuse without making NHibernate think that I need to associate the mappings for any reason. It doesn't look like that's possible.
Nick Swarr
I updated my answer, hope it helps.
Jamie Ide
Ah, I made a copy/paste error. Fixed it. I actually have exactly what you suggested. FooAuditMap inherits from ClassMap<FooAudit>. When I have FooAuditMap then it creates the double query behavior I described above. When I remove it, it goes away. There's no connection between the objects in the config or the mapping but querying Foo creates the two queries.
Nick Swarr
+1  A: 

What you are seeing is the expected behavior.

Querying a base class also queries any inherited classes.

If there is an explicit NHibernate mapping for that (subclass, joined-subclass, etc) that'll be just one query. Otherwise, it's considered an implicitly polymorphic definition, and two queries are issued to return results from all of them.

I believe (from the docs; I haven't tried) that you can avoid that by mapping the class with polymorphism="explicit". I don't know if Fluent supports it.

Diego Mijelshon
Awesome, thanks!
Nick Swarr