【问题标题】:many-to-one, all-delete orphan, set property to null but entity not deleted多对一,全部删除孤儿,将属性设置为 null 但实体未删除
【发布时间】:2013-10-22 22:25:54
【问题描述】:

使用 NHibernate v3.0。我有一个类似的课程:

class Foo
{
  bool barActive;
  Bar bar;
}

Bar 实例完全由 Foo 内部管理:

  1. 当“barActive”为真时,“bar”设置为 Bar 实例。
  2. 当“barActive”设置为 false 时,“bar”字段设置为 null。

Foo.bar 的映射如下:

<many-to-one name="bar" column="BarId" cascade="all-delete-orphan" unique="true" />

但是,当“bar”设置为null时,它不会删除数据库中的Bar记录。 Bar 是一个在其他地方也使用的继承类,所以我不能只将此字段作为组件。

我本来希望“唯一”约束 +“删除孤儿”来处理这个问题。我是否遗漏了什么,或者 NHibernate 不能透明地处理这个?如果不能,似乎我唯一的选择是引发一个事件,以便更高级别的范围可以调用 ISession.Delete(bar)。

【问题讨论】:

  • a many-to-one 映射没有级联中的 all-delete-orphan 选项。只有all|none|save-update|delete。见这里nhforge.org/doc/nh/en/index.html#mapping-declaration-manytoone
  • 那为什么 NHibernate 接受映射没有错误呢?无论如何,你有办法完成我正在寻找的东西吗?
  • 据我所知,NHibernate 无法自动删除 many-to-one 关系的孤儿,因为代码中没有检查。如前所述,它将级联其他 4 个。有这张 jira 票 216.121.112.228/browse/NH-1262 我认为是相关的,但是您可以专门针对您的问题打开另一个票。

标签: nhibernate


【解决方案1】:

我有一个解决方法,它会自动删除孤儿。我相信它应该适用于 NHibernate 版本 3 及更高版本。它使用一个拦截器——基本上是一个处理各种与会话相关的事件的对象。当它检测到Foo 上的更新操作时,它将为孤立的Bar 添加显式删除。

using System;
using System.Collections;
using NHibernate;
using NHibernate.Type;

class Interceptor : EmptyInterceptor
{
    private ISession _session;
    private Bar _barOrphan;

    public override void SetSession(ISession session)
    {
        base.SetSession(session);
        _session = session;
    }

    public override bool OnFlushDirty(object entity, object id, object[] currentStates, object[] previousStates, string[] propertyNames, IType[] types)
    {
        if (entity.GetType() != typeof(Foo)) return;

        for (var i = 0; i < propertyNames.Length; i++)
        {
            if (!StringComparer.Ordinal.Equals(propertyNames[i], "bar")) continue;

            object previousState = previousStates[i];
            if (currentStates[i] != previousState)
            {
                _barOrphan = (Bar) previousState;
            }
            break;
        }
    }

    public override void PostFlush(ICollection entities)
    {
        if (_barOrphan == null) return;

        _session.Delete(_barOrphan);
        _barOrphan = null;

        _session.Flush();
    }
}

现在,当打开您的 NHibernate 会话时,您必须使用接受拦截器实例作为参数的重载之一,例如

using (ISession session = YourSessionFactoryGoesHere.OpenSession(new Interceptor()))
{
    ...
}

请注意,这只是一个草稿,用于解释这个概念(我希望我在重写代码时没有搞砸代码 ;-)。在实际使用场景中,您可能必须处理在一个工作单元中创建的可能的多个孤儿(同一实体上的事件,例如 Foo 可能有 bar1bar2!),所以而不是单个 @ 987654329@ 成员,您将需要在PostFlush() 中执行删除操作的队列。与其硬编码所涉及的类的类型和属性名称bar,不如使用泛型和属性选择器(例如PropertySelector.GetPropertyName&lt;Foo&gt;(foo =&gt; foo.bar),参见this link。DB 约束可能是一个问题,移动对Interceptor.PreFlush() 的删除操作可能会有所帮助,但我没有对其进行测试。不要忘记性能影响(例如,为每个更新的实体调用OnFlushDirty(),所以不要让它成为瓶颈)。

【讨论】:

    猜你喜欢
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-02
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多