How do I change a child's parent in NHibernate when cascade is delete-all-orphan?

后端 未结 1 937
鱼传尺愫
鱼传尺愫 2021-01-02 00:00

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

public class Storage
{
    public IList Boxes { get; set; }
}

public class Box
         


        
相关标签:
1条回答
  • 2021-01-02 00:35

    See http://fabiomaulo.blogspot.com/2009/09/nhibernate-tree-re-parenting.html

    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.

    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>
    

    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;
        }
    }
    

    ... 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;
        }
    }
    

    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.

    0 讨论(0)
提交回复
热议问题