These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Parent-Childs bidirectional association
PostPosted: Mon Sep 08, 2008 5:27 am 
Newbie

Joined: Mon Sep 08, 2008 4:54 am
Posts: 3
Hello,

I've a Parent-Childs bidirectional association.
In child.hbm.xml, default-cascade="all" so if I save a child, a transient parent attached is also saved (automatically by cascade).

On the other side, if I’ve a transient parent and add a child to his collection, I need to do these 2 operations before saving:
Mynewparent.Childs.Add(MynewChild);
MynewChild.Parent=this;

So, in my Parent class, I should have a AddChild() which contains the two lines above. Bu I also have a Get property for the _childs collection. Someone can use this collection to add a child and forget to do “MynewChild.Parent=this;“
The question is how to prevent this problem? How to allow the Get on the collection but force to add “this” to the added child?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 08, 2008 10:31 am 
Regular
Regular

Joined: Tue Oct 16, 2007 9:45 am
Posts: 93
Observable Collections. You will have to create your own persistent bag.

Code:
       public virtual IBag<billing.LineItem> lineItems
        {
            get
            {
                return _lineItems;
            }

            set
            {
                var oldValue = _lineItems;
                if (_lineItems != value)
                {

                    _lineItems = value;

                    //synchronize biderectional relationship
                    foreach (billing.LineItem obj in value)
                    {
                        if (Object.ReferenceEquals(obj.invoice, this) == false)
                        {
                            obj.invoice = this;
                        }
                    }

                    lineItems.BagChanged += new EventHandler<CollectionChangedEventArgs<billing.LineItem>>(lineItems_BagChanged);
                    OnPropertyChanged("lineItems");
                }
            }
        }



   private void lineItems_BagChanged(object sender, CollectionChangedEventArgs<billing.LineItem> e)
        {
            if (e.ChangeType == CollectionChangeType.ItemAdded)
            {
                if (Object.ReferenceEquals(e.InvolvedItem.invoice, this) == false)
                {
                    e.InvolvedItem.invoice = this;
                }

            }
            else if (e.ChangeType == CollectionChangeType.ItemRemoved)
            {
                e.InvolvedItem.invoice = null;


            }
        }


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 09, 2008 4:55 am 
Newbie

Joined: Mon Sep 08, 2008 4:54 am
Posts: 3
I know nothing about BAG. I use SET for all my colletions.
Thanks to you I know that I can exploit the track of the custom collections.

Thank you exp2000


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 09, 2008 8:49 am 
Regular
Regular

Joined: Tue Oct 16, 2007 9:45 am
Posts: 93
There is more to it though.Here is PersistentBag implementation. Ask if you have more questions as it took me a while to figure this whole thing out.

Code:
public class PersistentBag<T> : PersistentGenericBag<T>, .IBag<T>
    {
        public PersistentBag(ISessionImplementor session) : base(session) { }

        public PersistentBag(ISessionImplementor session, IList<T> coll) : base(session, coll) { }

        #region Events
        public event EventHandler<CollectionChangedEventArgs<T>> BagChanged;
        #endregion

        #region IBag<T> interface
           here comes your implementation of the interface
           just two methods to see how to add and remove

  public new void Add(T item)
        {
            //Console.WriteLine("Bag.Add() start");

            if (item == null)
            {
                throw new ArgumentNullException("item", "item can't be null");
            }
            this.PerformAdd(item);

            //Console.WriteLine("Bag.Add() end");
        }

      public new bool Remove(T item)
        {

            bool removed = false;
            int index = this.IndexOf(item);
            if (index >= 0)
            {
                removed = base.Remove(item);
                if (!removed)
                {
                    throw new ApplicationException("The remove failed, as List<T>.Remove returned false");
                }
            }
            this.OnBagChanged(item, CollectionChangeType.ItemRemoved, index);
            this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index);

            return removed;
        }


        #end region

    #region Helper Methods

        protected int PerformAdd(T item)
        {
            base.Add(item);
            int idx = this.Count - 1;

            OnBagChanged(item, CollectionChangeType.ItemAdded, idx);
            OnCollectionChanged(NotifyCollectionChangedAction.Add, item, idx);
           // Console.WriteLine("EntityBag.PerformAdd() end");

            return idx;
        }

        /// <summary>
        /// Raises the <see cref="BagChanged"/> event to indicate that item(s)
        /// have been added to, or removed from, this collection.
        /// </summary>
        protected virtual void OnBagChanged(T involvedItem, CollectionChangeType action, int index)
        {
            if (BagChanged != null)
            {
                BagChanged(this, new CollectionChangedEventArgs<T>(involvedItem, action, index));
            }
        }

     #region INotifyCollectionChanged

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        /// <summary>
        /// Raises the <see cref="CollectionChanged"/> event to indicate that item(s)
        /// have been added to, or removed from, this collection.
        /// </summary>
        protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, object changedItem, int index)
        {
            if (CollectionChanged != null)
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem, index));
        }

        #endregion


You will have to implement custom type like this

Code:
  /// <summary>
    /// The NHibernate type for a generic bag collection that fires events
    /// when item(s) have been added to or removed from the collection.
    /// </summary>
    /// <typeparam name="T">The type of items in the bag</typeparam>
    public class PersistentBagType<T> : IUserCollectionType
    {
        #region IUserCollectionType Members

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

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

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

        public object Instantiate(int anticipatedSize)
        {
            //TODO: check where is this size comming from, NH does not seem to use it at all
            return new Bag<T>();
        }

        public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
        {
            return new PersistentBag<T>(session);
        }

        public object ReplaceElements(object original, object target, ICollectionPersister persister, object owner,
                                      IDictionary copyCache, ISessionImplementor session)
        {
            IList<T> result = (IList<T>)target;
            result.Clear();
            foreach (object item in ((IEnumerable)original))
                result.Add((T)item);
            return result;
        }

        public IPersistentCollection Wrap(ISessionImplementor session, object collection)
        {
            return new PersistentBag<T>(session, (IBag<T>)collection);
        }

        #endregion
    }


and then in mapping files you will have to map it like this
Code:
    <!-- m 4 -->
    <bag name="lineItems" inverse="true" lazy="true" table="LINE_ITEM" cascade="all"
        collection-type="MyCompany.NHibernate.Collections.PersistentBagType`1[
            [MyCompany.AR200.Core.billing.LineItem, MyCompany.AR200.Core]], MyCompany.NHibernate">
      <key column="INVOICE_VEOID"/>
      <one-to-many class="MyCompany.AR200.Core.billing.LineItem"/>
    </bag>


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.