当层叠为全删除孤儿时,如何在NHibernate中更改孩子的父母? [英] How do I change a child's parent in NHibernate when cascade is delete-all-orphan?

查看:52
本文介绍了当层叠为全删除孤儿时,如何在NHibernate中更改孩子的父母?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个处于一对多关系的实体:

I have two entities in a bi-directional one-to-many relationship:

public class Storage
{
    public IList<Box> Boxes { get; set; }
}

public class Box
{
    public Storage CurrentStorage { get; set; }
}

以及映射:

<class name="Storage">
    <bag name="Boxes" cascade="all-delete-orphan" inverse="true">
        <key column="Storage_Id" />
        <one-to-many class="Box" />
    </bag>
</class>

<class name="Box">
    <many-to-one name="CurrentStorage" column="Storage_Id" />
</class>

一个Storage可以有多个Boxes,但是一个Box只能属于一个Storage. 我对它们进行了映射,以便一对多具有all-delete-orphan的级联.

A Storage can have many Boxes, but a Box can only belong to one Storage. I have them mapped so that the one-to-many has a cascade of all-delete-orphan.

我尝试更改Box的Storage时出现问题.假设我已经运行了这段代码:

My problem arises when I try to change a Box's Storage. Assuming I already ran this code:

var storage1 = new Storage();
var storage2 = new Storage();
storage1.Boxes.Add(new Box());

Session.Create(storage1);
Session.Create(storage2);

以下代码将给我一个例外:

The following code will give me an exception:

// get the first and only box in the DB
var existingBox = Database.GetBox().First();

// remove the box from storage1
existingBox.CurrentStorage.Boxes.Remove(existingBox);

// add the box to storage2 after it's been removed from storage1
var storage2 = Database.GetStorage().Second();
storage2.Boxes.Add(existingBox);

Session.Flush(); // commit changes to DB

我收到以下异常:

NHibernate.ObjectDeletedException:已删除的对象将通过级联重新保存(从关联中删除已删除的对象)

NHibernate.ObjectDeletedException : deleted object would be re-saved by cascade (remove deleted object from associations)

发生此异常是因为我将级联设置为all-delete-orphan.第一个Storage检测到我从其集合中删除了Box并将其标记为删除.但是,当我将其添加到第二个Storage(在同一会话中)时,它会尝试再次保存该框,并抛出ObjectDeletedException.

This exception occurs because I have the cascade set to all-delete-orphan. The first Storage detected that I removed the Box from its collection and marks it for deletion. However, when I added it to the second Storage (in the same session), it attempts to save the box again and the ObjectDeletedException is thrown.

我的问题是,如何在不遇到此异常的情况下获取Box来更改其父级Storage?我知道一个可能的解决方案是将级联更改为仅all,但是随后我失去了让NHibernate自动删除Box的能力,方法是简单地将其从Storage中删除,而不将其与另一个重新关联.还是这是唯一的方法,我必须在包装盒上手动调用Session.Delete才能删除它?

My question is, how do I get the Box to change its parent Storage without encountering this exception? I know one possible solution is to change the cascade to just all, but then I lose the ability to have NHibernate automatically delete a Box by simply removing it from a Storage and not re-associating it with another one. Or is this the only way to do it and I have to manually call Session.Delete on the box in order to remove it?

推荐答案

请参见 http: //fabiomaulo.blogspot.com/2009/09/nhibernate-tree-re-parenting.html

基本上,可以归结为这...您需要为NHibernate定义一个自定义收集类型,以重新定义成为孤儿的含义. NHibernate的默认行为是按照您发现的方式进行-如果已将其从父级中移除,则将其视为孤儿.相反,您需要NHibernate来测试子项,以查看其是否已分配给新的父项. NHibernate默认情况下不执行此操作,因为它需要有关一对多映射的其他信息-它需要知道子级上对应的多对一属性的名称.

Basically, it boils down to this... You need to define a custom collection type for NHibernate that re-defines what it means to be an orphan. NHibernate's default behavior is to do just as you discovered - to consider a child to be orphaned if it has been removed from the parent. Instead, you need NHibernate to test the child to see if it has been assigned to a new parent. NHibernate does not do this by default because it would require additional information on the one-to-many mapping - it would need to know the name of the corresponding many-to-one property on the child.

更改您的Storage映射,使其看起来像这样:

Change your Storage mapping to look like this:

<class name="Storage">
    <bag name="Boxes" cascade="all-delete-orphan" inverse="true" collection-type="StorageBoxBag">
        <key column="Storage_Id" />
        <one-to-many class="Box" />
    </bag>
</class>

定义一个名为StorageBoxBag的新类型(注意-此代码是针对NHibernate 2.1编写的-如果您使用的是NH3,则可能需要对其进行一些调整):

Define a new type named StorageBoxBag (note - this code is written against NHibernate 2.1 - if you are using NH3 you may have to tweak this a bit):

public class StorageBoxBag : IUserCollectionType
{
    public object Instantiate(int anticipatedSize)
    {
        return new List<Box>();
    }

    public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
    {
        return new PersistentStorageBoxBag(session);
    }

    public IPersistentCollection Wrap(ISessionImplementor session, object collection)
    {
        return new PersistentStorageBoxBag(session, (IList<Box>)collection);
    }

    public IEnumerable GetElements(object collection)
    {
        return (IEnumerable)collection;
    }

    public bool Contains(object collection, object entity)
    {
        return ((IList<Box>)collection).Contains((Box)entity);
    }

    public object IndexOf(object collection, object entity)
    {
        return ((IList<Box>) collection).IndexOf((Box) entity);
    }

    public object ReplaceElements(object original, object target, ICollectionPersister persister, object owner, IDictionary copyCache, ISessionImplementor session)
    {
        var result = (IList<Box>)target;
        result.Clear();

        foreach (var box in (IEnumerable)original)
            result.Add((Box)box);

        return result;
    }
}

...和一个名为PersistentStorageBoxBag的新类型:

... and a new type named PersistentStorageBoxBag:

public class PersistentStorageBoxBag: PersistentGenericBag<Box>
{
    public PersistentStorageBoxBag(ISessionImplementor session)
        : base(session)
    {
    }

    public PersistentStorageBoxBag(ISessionImplementor session, ICollection<Box> original)
        : base(session, original)
    {
    }

    public override ICollection GetOrphans(object snapshot, string entityName)
    {
        var orphans = base.GetOrphans(snapshot, entityName)
            .Cast<Box>()
            .Where(b => ReferenceEquals(null, b.CurrentStorage))
            .ToArray();

        return orphans;
    }
}

GetOrphans方法是魔术发生的地方.我们向NHibernate询问它认为 是孤儿的Box es的列表,然后将其过滤到仅实际上是 Box es的集合.

The GetOrphans method is where the magic happens. We ask NHibernate for the list of Boxes that it thinks are orphans, and then filter that down to only the set of Boxes that actually are orphans.

这篇关于当层叠为全删除孤儿时,如何在NHibernate中更改孩子的父母?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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