NHibernate 多对一映射:如果parent为null,则将外键设置为空Guid而不是null

问题描述 投票:0回答: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" 属性,如果是这样,我如何准确维护这些关系?

提前感谢您的帮助。

nhibernate mapping guid foreign-key-relationship many-to-one
2个回答
3
投票

如果我理解正确的话,你的数据库结构并没有强制执行引用完整性(FK_OwnerID 并不是真正的外键),但是通过你对映射的更改,你告诉 NHibernate 强制执行这一点。我认为这行不通。

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

  1. 使用您的创建一个虚拟条目 000-DB 中的 Guid 充当 所有这些孤儿的父母。
  2. 更改表以允许该列中存在 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


0
投票

我通过将多对一映射到受保护的属性并使用公共属性作为其周围的包装来解决这个问题。我还将外键映射为属性 映射:

类: 公共虚拟 Guid RefId{ get;放; }
受保护的虚拟 SomeObject RefObjectInternal{ get;放; } 公共虚拟SomeObject SomeObject { 得到 { if (RefId== Guid.Empty) 返回 null; 否则返回 RefObjectInternal; } 放 { RefObjectInternal=值; 如果(值==空) { RefId= Guid.Empty; } 否则 RefId = value.Id; } }

© www.soinside.com 2019 - 2024. All rights reserved.