-->
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.  [ 11 posts ] 
Author Message
 Post subject: ObservableCollection<T>
PostPosted: Mon Jan 08, 2007 5:00 am 
Newbie

Joined: Mon Jan 08, 2007 3:44 am
Posts: 3
Hello,

Is there any way to use System.Collections.ObjectModel.ObservableCollection<T> as type of a persistent Entity's property ?

The reason why I want to use ObservableCollection<T> is that it implements INotifyCollectionChanged & INotifyPropertyChanged and these interfaces are well suported by WPF.

I'm new on NHibernate, so any help or comments are welcome. :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 24, 2007 7:45 pm 
Newbie

Joined: Wed Jan 24, 2007 7:37 pm
Posts: 3
Location: Houston Metro, TX, USA
I've worked out a solution for this issue. I developed some collection classes which implement NHibernate's IUserCollectionType and WPF's INotifyCollectionChanged interface that will trigger UI updates when their contents change. Check out my blog article for more info and to download my demo project:

http://analog-man.blogspot.com/2007/01/bridge-gap-between-your-nhibernate.html


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 24, 2007 9:15 pm 
Newbie

Joined: Mon Jan 08, 2007 3:44 am
Posts: 3
Hi gdereese,

I'm very interested in your project, but the link for downloading doesn't work on your blog.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 24, 2007 10:07 pm 
Newbie

Joined: Wed Jan 24, 2007 7:37 pm
Posts: 3
Location: Houston Metro, TX, USA
FRED.G wrote:
Hi gdereese,

I'm very interested in your project, but the link for downloading doesn't work on your blog.


Sorry about that...I've updated the blog article with a working link. Try it again and let me know if you have any more problems.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 24, 2007 11:45 pm 
Newbie

Joined: Mon Jan 08, 2007 3:44 am
Posts: 3
Your demo is working well !

I really thank you because your code is the one I was waiting for.

I'm a french VB.Net programmer and I didn't have enough time for studying NHibernate's source code, so i gave up the idea of learning and using Nhibernate for my WPF project.
But thanks to your work, I am again interested by Nhibernate. Maybe, the translation of your code in VB.NET will be a good exercice for me to learn how to use Nhibernante with WPF databinding without changing Nhibernante's source code. I think I will try to do this later (I hope next month), because i'm actually on another project.

If I re-use or translate your code in vb.Net / French in a future project, I will mention your name and post a comment here or on your blog before.

Greetings


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 06, 2007 11:15 am 
Beginner
Beginner

Joined: Tue Sep 04, 2007 12:36 pm
Posts: 23
gdereese wrote:
FRED.G wrote:
Hi gdereese,

I'm very interested in your project, but the link for downloading doesn't work on your blog.


Sorry about that...I've updated the blog article with a working link. Try it again and let me know if you have any more problems.



gdereese, I would like to thank you for the sample project you shared with us it was very informative, but in my opinion, it bears some room for improvement.

The custom collection you have defined is certainly observeable, but it does not support lazy loading. You compensate for this lack of lazy loading by making the objects inside of the collection lazy proxies, and unfortunately, this is unacceptable for my needs.

I found some code from Damon Carr that I liked better, located at:
http://damon.agilefactor.com/2007/07/nhibernate-custom-collections.html

But the code that he shares does not support the observable collection quality that you supplied.

I put the two of these examples together to make a lazy-loading observeable custom collection with NHibernate, and the code looks like this:
Code:
namespace NotifyingCollectionDemo.Library.Collections
{
    public interface IDomainList<T> : IList<T>, IPersistentCollection, INotifyCollectionChanged
    {
    }
    internal class DomainPersistentGenericBag<T> : PersistentGenericBag<T>, IDomainList<T>
    {
        public DomainPersistentGenericBag(ISessionImplementor session, IList<T> coll)
            : base(session, coll)
        {
        }
        public DomainPersistentGenericBag(ISessionImplementor session)
            : base(session)
        {
        }

        #region INotifyCollectionChanged Members
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate an item has been
        /// added to the end of the collection.
        /// </summary>
        /// <param name="item">Item added to the collection.</param>
        protected void OnItemAdded(T item)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                                 NotifyCollectionChangedAction.Add, item, this.Count - 1));
            }
        }
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate the collection
        /// has been reset.  This is used when the collection has been cleared or
        /// entirely replaced.
        /// </summary>
        protected void OnCollectionReset()
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                            NotifyCollectionChangedAction.Reset));
            }
        }
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
        /// been inserted into the collection at the specified index.
        /// </summary>
        /// <param name="index">Index the item has been inserted at.</param>
        /// <param name="item">Item inserted into the collection.</param>
        protected void OnItemInserted(int index, T item)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                            NotifyCollectionChangedAction.Add, item, index));
            }
        }
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
        /// been removed from the collection at the specified index.
        /// </summary>
        /// <param name="item">Item removed from the collection.</param>
        /// <param name="index">Index the item has been removed from.</param>
        protected void OnItemRemoved(T item, int index)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                                 NotifyCollectionChangedAction.Remove, item, index));
            }
        }
        #endregion

        #region IList<T> members
        public new void Add(T item)
        {
            base.Add(item);
            this.OnItemAdded(item);
        }
        public new void Clear()
        {
            base.Clear();
            this.OnCollectionReset();
        }
        public new void Insert(int index, T item)
        {
            base.Insert(index, item);
            this.OnItemInserted(index, item);
        }
        public new bool Remove(T item)
        {
            int index = this.IndexOf(item);
            bool result = base.Remove(item);
            this.OnItemRemoved(item, index);
            return result;
        }
        public new void RemoveAt(int index)
        {
            T item = this[index];
            base.RemoveAt(index);
            this.OnItemRemoved(item, index);
        }
        #endregion
    }
    public class DomainList<T> : List<T>, IUserCollectionType, IDomainList<T>
    {
        private IPersistentCollection m_EncapsulatedCollection;
        private ISessionImplementor m_SessionImplementor;
        private bool m_IsReadOnly;
        public DomainList()
        {
        }
        public DomainList(IList<T> collection, ISessionImplementor session)
        {
            InitializeFromExisting(collection, session);
        }

        #region IDomainList<T> Members
        public void InitializeFromCache(ICollectionPersister persister, object disassembled, object owner)
        {
            m_EncapsulatedCollection.InitializeFromCache(persister, disassembled, owner);
        }
        public bool NeedsRecreate(ICollectionPersister persister)
        {
            return m_EncapsulatedCollection.NeedsRecreate(persister);
        }
        public IEnumerable Entries()
        {
            return m_EncapsulatedCollection.Entries();
        }
        public object ReadFrom(IDataReader reader, ICollectionPersister persister, ICollectionAliases descriptor,
                               object owner)
        {
            return m_EncapsulatedCollection.ReadFrom(reader, persister, descriptor, owner);
        }
        public object GetIndex(object entry, int i)
        {
            return m_EncapsulatedCollection.GetIndex(entry, i);
        }
        public object GetElement(object entry)
        {
            return m_EncapsulatedCollection.GetElement(entry);
        }
        public object GetSnapshotElement(object entry, int i)
        {
            return m_EncapsulatedCollection.GetSnapshotElement(entry, i);
        }
        public void BeforeInitialize(ICollectionPersister persister)
        {
            m_EncapsulatedCollection.BeforeInitialize(persister);
        }
        public bool EqualsSnapshot(IType elementType)
        {
            return m_EncapsulatedCollection.EqualsSnapshot(elementType);
        }
        public object Disassemble(ICollectionPersister persister)
        {
            return m_EncapsulatedCollection.Disassemble(persister);
        }
        public bool EntryExists(object entry, int i)
        {
            return m_EncapsulatedCollection.EntryExists(entry, i);
        }
        public bool NeedsInserting(object entry, int i, IType elemType)
        {
            return m_EncapsulatedCollection.NeedsInserting(entry, i, elemType);
        }
        public bool NeedsUpdating(object entry, int i, IType elemType)
        {
            return m_EncapsulatedCollection.NeedsUpdating(entry, i, elemType);
        }
        public ICollection GetDeletes(IType elemType, bool indexIsFormula)
        {
            return m_EncapsulatedCollection.GetDeletes(elemType, indexIsFormula);
        }
        public bool IsWrapper(object collection)
        {
            return m_EncapsulatedCollection.IsWrapper(collection);
        }
        public ICollection GetOrphans(object snapshot, Type entityName)
        {
            return m_EncapsulatedCollection.GetOrphans(snapshot, entityName);
        }
        public bool Empty
        {
            get { return m_EncapsulatedCollection.Empty; }
        }
        public void PostAction()
        {
            m_EncapsulatedCollection.PostAction();
        }
        public object GetValue()
        {
            return m_EncapsulatedCollection.GetValue();
        }
        public void BeginRead()
        {
            m_EncapsulatedCollection.BeginRead();
        }
        public bool EndRead(ICollectionPersister persister)
        {
            return m_EncapsulatedCollection.EndRead(persister);
        }
        public bool AfterInitialize(ICollectionPersister persister)
        {
            return m_EncapsulatedCollection.AfterInitialize(persister);
        }
        public bool UnsetSession(ISessionImplementor session)
        {
            return m_EncapsulatedCollection.UnsetSession(session);
        }
        public bool SetCurrentSession(ISessionImplementor session)
        {
            return m_EncapsulatedCollection.SetCurrentSession(session);
        }
        public ICollection GetSnapshot(ICollectionPersister persister)
        {
            return m_EncapsulatedCollection.GetSnapshot(persister);
        }
        public void ForceInitialization()
        {
            m_EncapsulatedCollection.ForceInitialization();
        }
        public void PreInsert(ICollectionPersister persister)
        {
            m_EncapsulatedCollection.PreInsert(persister);
        }
        public void AfterRowInsert(ICollectionPersister persister, object entry, int i)
        {
            m_EncapsulatedCollection.AfterRowInsert(persister, entry, i);
        }
        public object GetIdentifier(object entry, int i)
        {
            return m_EncapsulatedCollection.GetIdentifier(entry, i);
        }
        public bool IsSnapshotEmpty(ICollection snapshot)
        {
            return m_EncapsulatedCollection.IsSnapshotEmpty(snapshot);
        }
        public void ClearDirty()
        {
            m_EncapsulatedCollection.ClearDirty();
        }
        public void Dirty()
        {
            m_EncapsulatedCollection.Dirty();
        }
        public bool IsDirectlyAccessible
        {
            get { return m_EncapsulatedCollection.IsDirectlyAccessible; }
            set { m_EncapsulatedCollection.IsDirectlyAccessible = value; }
        }
        public bool WasInitialized
        {
            get { return m_EncapsulatedCollection.WasInitialized; }
        }
        public bool HasQueuedAdds
        {
            get { return m_EncapsulatedCollection.HasQueuedAdds; }
        }
        public ICollection QueuedAddsCollection
        {
            get { return m_EncapsulatedCollection.QueuedAddsCollection; }
        }
        public ICollectionSnapshot CollectionSnapshot
        {
            get { return m_EncapsulatedCollection.CollectionSnapshot; }
            set { m_EncapsulatedCollection.CollectionSnapshot = value; }
        }
        public object Owner
        {
            get { return m_EncapsulatedCollection.Owner; }
            set { m_EncapsulatedCollection.Owner = value; }
        }
        public bool IsDirty
        {
            get { return m_EncapsulatedCollection.IsDirty; }
        }
        #endregion

//        public IPersistentCollection InitializeFromExisting(IList<T> collection)
//        {
//            InitializeFromExisting(collection, m_SessionImplementor);
//            return m_EncapsulatedCollection;
//        }
        public IPersistentCollection InitializeFromExisting(IList<T> collection, ISessionImplementor session)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");
            if (session != null)
                m_EncapsulatedCollection = new DomainPersistentGenericBag<T>(session, collection);
            else
                throw new NullReferenceException("No ISessionImplementor Set");
            return m_EncapsulatedCollection;
        }

        #region IUserCollectionType Members
        IPersistentCollection IUserCollectionType.Instantiate(ISessionImplementor session,
                                                              ICollectionPersister persister)
        {
            m_EncapsulatedCollection = new DomainPersistentGenericBag<T>(session);
            m_SessionImplementor = session;
            return m_EncapsulatedCollection;
        }
        IPersistentCollection IUserCollectionType.Wrap(ISessionImplementor session, object collection)
        {
            m_EncapsulatedCollection = new DomainPersistentGenericBag<T>(session, (DomainList<T>) collection);
            m_SessionImplementor = session;
            return m_EncapsulatedCollection;
        }
        IEnumerable IUserCollectionType.GetElements(object collection)
        {
            return (IEnumerable) collection;
        }
        bool IUserCollectionType.Contains(object collection, object entity)
        {
            return ((IList<T>) collection).Contains((T) entity);
        }
        object IUserCollectionType.IndexOf(object collection, object entity)
        {
            return ((IList<T>) collection).IndexOf((T) entity);
        }
        object IUserCollectionType.ReplaceElements(object original, object target, ICollectionPersister persister,
                                                   object owner, IDictionary copyCache, ISessionImplementor session)
        {
            IList<T> result = (IList<T>) target;
            result.Clear();
            foreach (T item in ((IList<T>) original))
                result.Add(item);
            return result;
        }
        object IUserCollectionType.Instantiate()
        {
            return new DomainList<T>();
        }
        #endregion

        #region INotifyCollectionChanged Members
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate an item has been
        /// added to the end of the collection.
        /// </summary>
        /// <param name="item">Item added to the collection.</param>
        protected void OnItemAdded(T item)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                                 NotifyCollectionChangedAction.Add, item, this.Count - 1));
            }
        }
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate the collection
        /// has been reset.  This is used when the collection has been cleared or
        /// entirely replaced.
        /// </summary>
        protected void OnCollectionReset()
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                                 NotifyCollectionChangedAction.Reset));
            }
        }
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
        /// been inserted into the collection at the specified index.
        /// </summary>
        /// <param name="index">Index the item has been inserted at.</param>
        /// <param name="item">Item inserted into the collection.</param>
        protected void OnItemInserted(int index, T item)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                                 NotifyCollectionChangedAction.Add, item, index));
            }
        }
        /// <summary>
        /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
        /// been removed from the collection at the specified index.
        /// </summary>
        /// <param name="item">Item removed from the collection.</param>
        /// <param name="index">Index the item has been removed from.</param>
        protected void OnItemRemoved(T item, int index)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                                 NotifyCollectionChangedAction.Remove, item, index));
            }
        }
        #endregion

        #region IList<T> members
        public new void Add(T item)
        {
            base.Add(item);
            this.OnItemAdded(item);
        }
        public new void Clear()
        {
            base.Clear();
            this.OnCollectionReset();
        }
        public new void Insert(int index, T item)
        {
            base.Insert(index, item);
            this.OnItemInserted(index, item);
        }
        public new bool Remove(T item)
        {
            int index = this.IndexOf(item);

            bool result = base.Remove(item);
            this.OnItemRemoved(item, index);

            return result;
        }
        public new void RemoveAt(int index)
        {
            T item = this[index];

            base.RemoveAt(index);
            this.OnItemRemoved(item, index);
        }
        #endregion
    }
}


How to use this? Inside of your persistent objects you would have code like this:
Code:
private IDomainList<ListItem> _items = new DomainList<ListItem>();
...
public  IDomainList<ListItem> Items
        {
            get { return this._items; }
            set { this._items = value; }
        }


and inside of your mapping files, you would use this:
Code:
<bag name="Items" inverse="true" cascade="all-delete-orphan" generic="true" lazy="true"
    collection-type="NotifyingCollectionDemo.Library.Collections.DomainList`1[[NotifyingCollectionDemo.Library.DomainModel.ListItem, NotifyingCollectionDemo.Library]], NotifyingCollectionDemo.Library">
    <key column="ListContainerID" />
      <one-to-many class="ListItem" />
    </bag>


The above code does work according to my tests, but still I am not happy. Do you know why? Look at the mapping syntax for collection-type: "`1[[..]]"
This is ugly, and it could become difficult to manage as things get more complex.

Why is it that when I use an IList<T> with a PersistentGenericBagthat I do not have to specify the data type of the collection class, but when I use my own custom collection (that inherits from PersistentGenericBag) I must specify this?

_________________
^^If this post helped you, make sure to rate it Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 11, 2007 9:51 pm 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
Why do both DomainPersistentGenericBag<T> and DomainList<T> implement IPersistentCollection? The transient collection type should not "know" about NHibernate as is described in another thread (http://forum.hibernate.org/viewtopic.php?t=972814#2346372).

In fact, I'm pretty sure gdereese's custom collection implementation does support lazy loading. His demo app does not use this facility, but if you put his custom collection into another program then you may find lazy loading works.

In any case, I agree with you that gdereese's sample project leaves room for improvement. That's why after studying his example I then created three types (bag, list, and set) of observable collections. See my blog entry (http://happynomad121.blogspot.com/2007/12/collections-for-wpf-and-nhibernate.html) for further details and to download the solution.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 12, 2007 11:20 am 
Beginner
Beginner

Joined: Tue Sep 04, 2007 12:36 pm
Posts: 23
HappyNomad wrote:
Why do both DomainPersistentGenericBag<T> and DomainList<T> implement IPersistentCollection? The transient collection type should not "know" about NHibernate as is described in another thread (http://forum.hibernate.org/viewtopic.php?t=972814#2346372).


I am interested in how you are going to have a lazily loaded collection without making a custom implementation of PersistentGenericBag. Remember, the transient collection that you create is different than the collection that NHibernate dynamically builds. look at the debugger if you dont believe me.

HappyNomad wrote:
In fact, I'm pretty sure gdereese's custom collection implementation does support lazy loading. His demo app does not use this facility, but if you put his custom collection into another program then you may find lazy loading works.


It is a collection of lazy objects, not a lazy collection of objects.

Look at the mappings... try taking the lazy proxy settings off of the listItem class and you will see what I mean.

The difference is that I want my lazy definition on the one-to-many mapping and not on the class.

Look in the comments on the bottom of gdresse's blog post, damon carr writes:
Quote:
Excellent work. However you realize your solution will not support lazy collections?

I am assuming you are as you explicitly set lazy to false in the mapping.

I have corrected this and am providing how to get the code below.

NOTE: I used this before so I do not have the observable interface but it would be trivial for you to add this for your article. If it serves you to repost with this option to allow Laziness this is open source freeware I created so go ahead.

For me, missing Lazy collections is a deal breaker. The cool thing is you can use this class over and over. I did the hard part already.


much respect to gdreese for giving me a starting point, but custom collections in NHibernate are a little more difficult than they seem.

Take a look at Billy Mccafferty's blog posts on this for a more detailed explanation:

http://devlicio.us/blogs/billy_mccaffer ... asics.aspx
http://devlicio.us/blogs/billy_mccaffer ... mp-gt.aspx
http://devlicio.us/blogs/billy_mccaffer ... tored.aspx

_________________
^^If this post helped you, make sure to rate it Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 13, 2007 2:30 am 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
PeteWeissbrod wrote:
I am interested in how you are going to have a lazily loaded collection without making a custom implementation of PersistentGenericBag.

gdereese's project does contain a custom PersistentGenericBag subclass. Look at his PersistentGenericObservableBag class.

PeteWeissbrod wrote:
Remember, the transient collection that you create is different than the collection that NHibernate dynamically builds. look at the debugger if you dont believe me.

I don't think NHibernate "dynamically builds" transient collection proxy classes. If a collection is transient, that means the domain code created it. If it's a persisted custom collection, then NHibernate has asked the IUserCollectionType specified in the mapping file to create what instances it needs.

PeteWeissbrod wrote:
It is a collection of lazy objects, not a lazy collection of objects.

I thought that PersistentGenericBag (which gdereese's project uses) handles both such scenerios.

PeteWeissbrod wrote:
Look at the mappings... try taking the lazy proxy settings off of the listItem class and you will see what I mean.
The difference is that I want my lazy definition on the one-to-many mapping and not on the class.

lazy="true" by default in NHibernate v1.2. Much of the documentation has not been updated to reflect this fact, which is definitely confusing.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 19, 2007 11:50 am 
Beginner
Beginner

Joined: Tue Sep 04, 2007 12:36 pm
Posts: 23
I think I've finally got a solution I am happy with.

I wrote about it here:
http://www.acceptedeclectic.com/2007/12/generic-custom-nhibernate-collections.html


Here are the basics:
I need a custom collection in NHibernate. I need to make two flavors of collections: one is transient and one is persistent. They implement a common interface (INotifyCollectionChanged). The transient one inherits from List(T) and the persistent one inherits from PersistentGenericBag(T).

The common interface:
Code:
using System.Collections.Generic;
using System.Collections.Specialized;

namespace NotifyingCollectionDemo.Library.Collections
{
public interface IDomainCollection<T>:INotifyCollectionChanged, IList<T>
{
}
}

The transient version:
Code:

using System.Collections.Generic;
using System.Collections.Specialized;

namespace NotifyingCollectionDemo.Library.Collections
{
public class TransientDomainCollection<T>:List<T>, IDomainCollection<T>
{
     #region INotifyCollectionChanged Members
     public event NotifyCollectionChangedEventHandler CollectionChanged;
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has been
     /// added to the end of the collection.
     /// </summary>
     /// <param name="item">Item added to the collection.</param>
     protected void OnItemAdded(T item)
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Add, item, this.Count - 1));
         }
     }
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate the collection
     /// has been reset.  This is used when the collection has been cleared or
     /// entirely replaced.
     /// </summary>
     protected void OnCollectionReset()
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Reset));
         }
     }
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
     /// been inserted into the collection at the specified index.
     /// </summary>
     /// <param name="index">Index the item has been inserted at.</param>
     /// <param name="item">Item inserted into the collection.</param>
     protected void OnItemInserted(int index, T item)
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Add, item, index));
         }
     }
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
     /// been removed from the collection at the specified index.
     /// </summary>
     /// <param name="item">Item removed from the collection.</param>
     /// <param name="index">Index the item has been removed from.</param>
     protected void OnItemRemoved(T item, int index)
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Remove, item, index));
         }
     }
     #endregion

     /// <summary>
     /// we need to re-implement the IList methods to support observability
     /// </summary>
     /// <param name="item"></param>
     #region IList<T> members
     public new void Add(T item)
     {
         base.Add(item);
         this.OnItemAdded(item);
     }
     public new void Clear()
     {
         base.Clear();
         this.OnCollectionReset();
     }
     public new void Insert(int index, T item)
     {
         base.Insert(index, item);
         this.OnItemInserted(index, item);
     }
     public new bool Remove(T item)
     {
         int index = this.IndexOf(item);
         bool result = base.Remove(item);
         this.OnItemRemoved(item, index);
         return result;
     }
     public new void RemoveAt(int index)
     {
         T item = this[index];
         base.RemoveAt(index);
         this.OnItemRemoved(item, index);
     }
     #endregion
}
}


The persistent version:
Code:
using System.Collections.Generic;
using System.Collections.Specialized;
using NHibernate.Collection.Generic;
using NHibernate.Engine;

namespace NotifyingCollectionDemo.Library.Collections
{
public class PersistentDomainCollection<T>:PersistentGenericBag<T>, IDomainCollection<T>
{
     #region constructors
     public PersistentDomainCollection(ISessionImplementor session, IList<T> coll) : base(session, coll)
     {
     }
     public PersistentDomainCollection(ISessionImplementor session) : base(session)
     {
     }
     #endregion

     #region INotifyCollectionChanged Members
     public event NotifyCollectionChangedEventHandler CollectionChanged;
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has been
     /// added to the end of the collection.
     /// </summary>
     /// <param name="item">Item added to the collection.</param>
     protected void OnItemAdded(T item)
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Add, item, this.Count - 1));
         }
     }
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate the collection
     /// has been reset.  This is used when the collection has been cleared or
     /// entirely replaced.
     /// </summary>
     protected void OnCollectionReset()
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Reset));
         }
     }
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
     /// been inserted into the collection at the specified index.
     /// </summary>
     /// <param name="index">Index the item has been inserted at.</param>
     /// <param name="item">Item inserted into the collection.</param>
     protected void OnItemInserted(int index, T item)
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Add, item, index));
         }
     }
     /// <summary>
     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
     /// been removed from the collection at the specified index.
     /// </summary>
     /// <param name="item">Item removed from the collection.</param>
     /// <param name="index">Index the item has been removed from.</param>
     protected void OnItemRemoved(T item, int index)
     {
         if (this.CollectionChanged != null)
         {
             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Remove, item, index));
         }
     }
     #endregion

     /// <summary>
     /// we need to re-implement the IList methods to support observability
     /// </summary>
     /// <param name="item"></param>
     #region IList<T> members
     public new void Add(T item)
     {
         base.Add(item);
         this.OnItemAdded(item);
     }
     public new void Clear()
     {
         base.Clear();
         this.OnCollectionReset();
     }
     public new void Insert(int index, T item)
     {
         base.Insert(index, item);
         this.OnItemInserted(index, item);
     }
     public new bool Remove(T item)
     {
         int index = this.IndexOf(item);
         bool result = base.Remove(item);
         this.OnItemRemoved(item, index);
         return result;
     }
     public new void RemoveAt(int index)
     {
         T item = this[index];
         base.RemoveAt(index);
         this.OnItemRemoved(item, index);
     }
     #endregion
}
}

Finally, we need an implementation of IUserCollectionType to tie this all together and use it in the mapping files. Notice how I treat this as a factory class:

Code:
using System.Collections;
using System.Collections.Generic;
using NHibernate.Collection;
using NHibernate.Engine;
using NHibernate.Persister.Collection;
using NHibernate.UserTypes;

namespace NotifyingCollectionDemo.Library.Collections
{
public class DomainCollectionFactory<T> :IUserCollectionType
{
     #region IUserCollectionType Members
     public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
     {
         return new PersistentDomainCollection<T>(session);
     }
     public IPersistentCollection Wrap(ISessionImplementor session, object collection)
     {
         return new PersistentDomainCollection<T>(session,collection as IList<T>);
     }
     public object Instantiate()
     {
         return new TransientDomainCollection<T>();
     }
     public IEnumerable GetElements(object collection)
     {
         return (IEnumerable) collection;
     }
     public bool Contains(object collection, object entity)
     {
         return ((IList) collection).Contains(entity);
     }
     public object IndexOf(object collection, object entity)
     {
         return ((IList) collection).IndexOf(entity);
     }
     public object ReplaceElements(object original, object target, ICollectionPersister persister,
                                   object owner, IDictionary copyCache, ISessionImplementor session)
     {
         IList result = (IList) target;
         result.Clear();
         foreach (object o in ((IEnumerable) original))
         {
             result.Add(o);
         }
         return result;
     }
     #endregion
}
}


The only place my PersistentCollection type is referenced is in my factory class. The only place my factory class is references is in my mapping files. This is cool because I have decoupled the NHibernate-specific collection code from my domain objects.

How to use this?
the mapping:
Code:
<bag name="Items" inverse="true" cascade="all-delete-orphan" generic="true" lazy="true"
collection-type=
      "NotifyingCollectionDemo.Library.Collections.DomainCollectionFactory`1[[NotifyingCollectionDemo.Library.DomainModel.ListItem, NotifyingCollectionDemo.Library]], NotifyingCollectionDemo.Library">
<key column="ListContainerID" />
   <one-to-many class="ListItem" />
</bag>

and the code
Code:
private IDomainCollection<ListItem> _items = new TransientDomainCollection<ListItem>();

        public IDomainCollection<ListItem> Items
        {
            get { return this._items; }
            set { this._items = value; }
        }


I like this one, do you like this one?

_________________
^^If this post helped you, make sure to rate it Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 20, 2007 3:36 pm 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
Yes, this one looks correct. The IUserCollectionType is in its own class which is good. Other than that and the class names, it actually looks pretty much the same as gdereese's project. You provide a clear explanation about how to implement a custom collection type, which I haven't seen elsewhere. If all you're interested in is creating is an observable collection, though, you could save yourself the trouble and just use my solution which includes all three types (bag, list, and set) of observable collections for NHibernate. The URL once again is http://happynomad121.blogspot.com/2007/12/collections-for-wpf-and-nhibernate.html.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 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.