2

我想要做的是非常直截了当,但我似乎无法使用 NHibernate 获得正确的映射。

我正在处理具有父对象和子对象的数据库。子对象具有对数据类型 Guid 的父主键的外键引用。在任何情况下都很正常。现在数据库的设置方式是外键字段永远不能为空,因此对于没有父对象的孤立对象,外键应该是空的 Guid ('00000000-0000-0000-0000-000000000000 ')。

我设置 Nhibernate 的方式长期以来一直运行良好,但最近我将关系设置为双向,然后开始出现问题。显然,NHibernate 会看到父级为 null 并尝试将 null 保存到外键字段,但这是不允许的!

我正在使用的关系映射的结构示例如下。

父方映射:

<id name="ID" column="ID">
    <generator class="guid" />
</id>
<bag name="Children" table="Children" lazy="false" cascade="all" inverse="true">
  <key column="FK_OwnerID" not-null="true"/>
  <one-to-many class="Childclass"/>
</bag>

子侧映射:

<many-to-one name="Owner" column="FK_OwnerID" not-found="ignore" not-null="false" class="OwnerClass"/>

我一直在尝试不同的属性,但无济于事。我是否被迫使用 insert="false" 和 update="false" 属性,如果是这样,我该如何准确地维护这些关系?

提前感谢您的帮助。

4

1 回答 1

3

如果我理解正确,您的数据库结构没有强制执行引用完整性(FK_OwnerID 并不是真正的外键),但是通过您在映射中的更改,您告诉 NHibernate 强制执行此操作。我认为那行不通。

基本上,我看到有两个选项可以解决这个问题:

  1. 在您的数据库中使用您的 000-Guid 创建一个虚拟条目,作为所有这些孤儿的父项。
  2. 更改您的表以在该列中允许 NULL 并使其成为真正的外键约束(使用“on cascade set null”)。(我建议这样做。)

编辑: 如果选项 1 不是一个选项,那么我建议选项 3:您可以尝试 NHibernate 的拦截器功能。您需要实现 IInterceptor(或从 EmptyInterceptor 继承)。OnSave() 方法用于新对象, OnFlushDirty 用于更改对象。我们在这里所做的是创建一个具有所需 ID 的新“虚拟”对象并分配它。

using System;
using NHibernate;

namespace NameSpaceWithDAL
{
    public class TestInterceptor : NHibernate.EmptyInterceptor
    {
        public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
        {
            if (entity is ChildClass && (entity as ChildClass).Owner == null)
            {
                SetState(propertyNames, state, "Owner", new OwnerClass { ID = "000..." });
            }

            return true;
        }    

        public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
        {
            if (entity is ChildClass && (entity as ChildClass).Owner == null)
            {
                SetState(propertyNames, state, "Owner", new OwnerClass { ID = "000..." });
            }

            return true;
        }

        private void SetState(string[] propertyNames, object[] state, string propertyname, object value)
        {
            var index = Array.IndexOf(propertyNames, propertyname);
            if (index == -1) return;
            state[index] = value;
        }    
    }
}

为了使用它,需要为会话(在 OpenSession() 中)或整体配置中定义拦截器:

new Configuration().SetInterceptor(new TestInterceptor());

我用简单的属性测试了上面的代码,所以我不能说它是否真的适用于关系。

取自Mike O'Brien的代码示例

于 2011-03-01T12:05:30.823 回答