NHibernate 和继承会造成意外的双重查询行为吗?

发布于 2024-09-07 20:34:37 字数 1740 浏览 1 评论 0原文

我正在使用 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 文件,并且绝对没有任何东西链接这些类。

编辑:为了完整起见,这就是引导程序配置的样子。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

追星践月 2024-09-14 20:34:37

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

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

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

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

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.

此生挚爱伱 2024-09-14 20:34:37

尝试:

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 即可。但是,您对 Foo 类型的对象的查询将不包含任何 FooAudit 类型,因为 NH 不知道这种关系。

但我认为这里是一对多的关系——Foo 有 FooAudit 的集合——你应该这样映射它。

更新 2:看来我之前的声明“但是您对 Foo 类型的对象的查询将不包含任何 FooAudit 类型,因为 NH 不知道这种关系。”是错误的。有一种方法可以通过使用 特殊类属性但这不是一个很好的解决方案。

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文