多对一,全删除的孤儿,将属性设置为null,但未删除实体 [英] many-to-one, all-delete orphan, set property to null but entity not deleted

查看:274
本文介绍了多对一,全删除的孤儿,将属性设置为null,但未删除实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Using NHibernate v3.0. I have a class similar to this:

class Foo
{
  bool barActive;
  Bar bar;
}

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

The Bar instance is managed entirely internally to Foo:

  1. 如果"barActive"为true,则将"bar"设置为Bar实例.
  2. 当"barActive"设置为false时,"bar"字段设置为null.

Foo.bar的映射方式如下:

Foo.bar is mapped like so:

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

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

However, when "bar" is set to null, it does not delete the Bar record in the database. Bar is an inherited class that is used elsewhere as well, so I can't just make this field a component.

我本来希望使用唯一"约束+删除-孤立"约束来处理此问题.我是否缺少某些东西,还是NHibernate不能透明地处理此问题?如果不能,看来我唯一的选择是引发一个事件,以便更高级别的作用域可以调用ISession.Delete(bar).

I would have expected the "unique" constraint + "delete-orphan" to handle this. Am I missing something, or can NHibernate not handle this transparently? If it can't, it seems my only option is to raise an event so a higher-level scope can call ISession.Delete(bar).

推荐答案

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

I have a workaround, that will automatically delete the orphan. I believe it should work for NHibernate version 3 and above. It uses an interceptor - basically an object that handles various session-related events. When it detects the update operation on Foo, it will add an explicit delete for the orphaned 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会话时,您必须使用接受拦截器实例作为参数的重载之一,例如

Now, when opening your NHibernate session, you have to use one of the overloads that accepts an interceptor instance as an argument, e.g.

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

请注意,这只是一个草稿,以解释该概念(希望我在编写代码时不会弄乱代码;-).在实际使用场景中,您可能必须处理在一个工作单元中创建的多个孤儿(同一实体上的事件,例如Foo可能具有bar1bar2!),因此,除了单个_barOrphan成员,您需要在PostFlush()中执行删除操作的队列.您将要使用泛型和属性选择器(例如,PropertySelector.GetPropertyName<Foo>(foo => foo.bar),请参见

Please note that this is just a draft, to explain the concept (I hope I did not screw up the code as I was rewriting it ;-). In a real usage scenario, you may have to deal with possible multiple orphans created in one unit of work (event on the same entity, e.g. Foo could have bar1 and bar2!), so instead of a single _barOrphan member you will need a queue of the delete actions to be executed in PostFlush(). Instead of hard-coding the types of the involved classes and the name of the property bar, you will want to use generics and a property selector (e.g. PropertySelector.GetPropertyName<Foo>(foo => foo.bar), see this link. DB constraint could be a problem, moving the delete operation to Interceptor.PreFlush() might help, but I did not test it. Don't forget about the performance impact (e.g. OnFlushDirty() is invoked for each updated entity, so don't make it a bottleneck).

这篇关于多对一,全删除的孤儿,将属性设置为null,但未删除实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆