NHibernate和继承创建意外的双查询行为?

时间:2010-06-30 15:55:48

标签: c# nhibernate fluent-nhibernate

我正在使用FluentNHibernate。我没有使用自动映射。我有一个子类的子类。当我查询基类时,它会对子类执行额外的查询。这是我正在做的(人为的)示例:

public class Foo
{
    int Id;
    string SomeValue;
}

我创建了另一个代表第一个审计记录的类,我继承了它:

public class FooAudit : Foo
{
    DateTime DateModified;
}

我为每个转到他们自己的表创建单独的映射:

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");
    }
}

我对Foo执行查询:

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

然后两次击中数据库,一次针对Foo执行该查询,再次执行FooAudit。

为什么要进行两次查询?我生成了HBM文件,并且绝对没有链接这些类。

编辑:为了完整性,这就是bootstrap配置的样子。

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();
}

2 个答案:

答案 0 :(得分:4)

您所看到的是预期的行为。

查询基类也会查询任何继承的类。

如果有一个明确的NHibernate映射(子类,连接子类等),那将只是一个查询。否则,它被认为是一个隐式的多态定义,并发出两个查询来返回所有这些结果。

我相信(来自文档;我没有尝试过)你可以通过用polymorphism="explicit"映射类来避免这种情况。我不知道Fluent是否支持它。

答案 1 :(得分:0)

尝试:

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");
    }
}

您仍然遇到问题,因为Foo和FooAudit的标识符不同。如果FooAudit是Foo的子类,它应该具有存储在Foo表中的相同标识符。

基于OP评论进行更新:您当然可以在域模型中拥有继承链,而无需在NHibnerate中表达它。只需更改FooAuditMap即可继承ClassMap<FooAudit>。但是,对Foo类型的对象的查询将不包含任何FooAudit类型,因为NH不知道该关系。

但我认为你在这里拥有的是一对多关系--Foo有一系列FooAudits - 你应该这样映射。

更新2:看来我之前的声明“但是你对Foo类型对象的查询将不包含任何FooAudit类型,因为NH不知道这种关系。”是错的。有一种方法可以通过使用special class property限制结果来返回基类,但这不是一个很好的解决方案。

我认为你最好摆脱继承,让两个类都实现一个接口或将公共属性映射为一个组件。