Dashboard > ActiveRecord > ... > Recipes > Alternative to using nhibernate custom collections
Alternative to using nhibernate custom collections
Added by Lee Henson, last edited by Lee Henson on Nov 19, 2007
Labels: 
(None)


If you are having trouble with using NHibernate's custom collections, possibly a simpler alternative is to wrap access to your bags within another collection:

private readonly IList<Foo> internalFoos = new List<Foo>();
private FooList foos;

[HasMany(typeof(Foo), Access = PropertyAccess.NosetterCamelcase, ColumnKey = "FooID", Inverse = true, Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, RelationType = RelationType.Bag)]
protected virtual IList<Foo> InternalFoos
{
    get { return internalFoos; }
}

public FooList Foos
{
    get
    {
        if (fooList == null)
            fooList = new FooList(internalFoos);

        return fooList;
    }
}

 You can then extend this FooList class with custom methods/properties in order to give you a richer model. FooList itself is simply a wrapper for the IList<Foo> instance, delegating all calls to that collection:

public class ListWrapper<T> : IList<T>, ICollection
    {
        private readonly IList<T> internalList;
        private object syncRoot;

        public ListWrapper()
        {
            internalList = new List<T>();
        }

        public ListWrapper(IList<T> internalList)
        {
            this.internalList = internalList;
        }

        public void AddRange(IEnumerable<T> range)
        {
            foreach (T t in range)
                internalList.Add(t);
        }

        public List<O> ConvertAll<O>(Converter<T, O> converter)
        {
            List<O> output = new List<O>();

            foreach (T t in internalList)
                output.Add(converter(t));

            return output;
        }

        public List<T> Copy()
        {
            List<T> copy = new List<T>();

            copy.AddRange(internalList);

            return copy;
        }

        public bool Exists(Predicate<T> match)
        {
            return Find(match) != null;
        }

        public bool Exists(ISpecification<T> match)
        {
            return Exists(match.IsSatisfiedBy);
        }

        public T Find(Predicate<T> match)
        {
            foreach (T t in internalList)
                if (match(t))
                    return t;

            return default(T);
        }

        public T Find(ISpecification<T> match)
        {
            return Find(match.IsSatisfiedBy);
        }

        public T FindAfterSort(IComparer<T> comparer, Predicate<T> match)
        {
            T[] elements = new T[internalList.Count];
            internalList.CopyTo(elements, 0);

            Array.Sort(elements, comparer);

            return Array.Find(elements, match);
        }

        public T FindAfterSort(IComparer<T> comparer, ISpecification<T> match)
        {
            return FindAfterSort(comparer, match.IsSatisfiedBy);
        }

        public List<T> FindAll(Predicate<T> match)
        {
            List<T> output = new List<T>();

            foreach (T t in internalList)
                if (match(t))
                    output.Add(t);

            return output;
        }

        public List<T> FindAll(ISpecification<T> match)
        {
            return FindAll(match.IsSatisfiedBy);
        }

        public List<T> FindAllAfterSort(IComparer<T> comparer, Predicate<T> match)
        {
            T[] elements = new T[internalList.Count];
            internalList.CopyTo(elements, 0);

            Array.Sort(elements, comparer);

            return new List<T>(Array.FindAll(elements, match));
        }

        public List<T> FindAllAfterSort(IComparer<T> comparer, ISpecification<T> match)
        {
            return FindAllAfterSort(comparer, match.IsSatisfiedBy);
        }

        public void ForEach(Action<T> action)
        {
            foreach (T t in internalList)
                action(t);
        }

        public List<T> Sort()
        {
            List<T> copy = Copy();

            copy.Sort();

            return copy;
        }

        public List<T> Sort(Comparison<T> comparison)
        {
            List<T> copy = Copy();

            copy.Sort(comparison);

            return copy;
        }

        public List<T> Sort(IComparer<T> comparer)
        {
            List<T> copy = Copy();

            copy.Sort(comparer);

            return copy;
        }

        public List<T> Sort(int index, int count, IComparer<T> comparer)
        {
            List<T> copy = Copy();

            copy.Sort(index, count, comparer);

            return copy;
        }

        public List<T> ToList()
        {
            List<T> output = new List<T>();

            output.AddRange(internalList);

            return output;
        }

        public bool TrueForAll(Predicate<T> match)
        {
            foreach (T t in internalList)
                if (!match(t))
                    return false;

            return true;
        }

        public bool TrueForAll(ISpecification<T> match)
        {
            return TrueForAll(match.IsSatisfiedBy);
        }

        public int IndexOf(T item)
        {
            return internalList.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            internalList.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            internalList.RemoveAt(index);
        }

        public T this[int index]
        {
            get { return internalList[index]; }
            set { internalList[index] = value; }
        }

        public void Add(T item)
        {
            internalList.Add(item);
        }

        public void Clear()
        {
            internalList.Clear();
        }

        public bool Contains(T item)
        {
            return internalList.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            internalList.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            return internalList.Remove(item);
        }

        void ICollection.CopyTo(Array array, int index)
        {
            if (internalList is ICollection)
                ((ICollection) internalList).CopyTo(array, index);
            else
            {
                if ((array != null) && (array.Rank != 1))
                    throw new ArgumentException("Multi-dimensional arrays are not allowed.", "array");

                try
                {
                    T[] array2 = new T[internalList.Count];
                    internalList.CopyTo(array2, 0);
                    Array.Copy(array2, index, array, 0, array2.Length - index);
                }
                catch (ArrayTypeMismatchException)
                {
                    throw new ArgumentException("Invalid array type", "array");
                }
            }
        }

        public int Count
        {
            get { return internalList.Count; }
        }

        object ICollection.SyncRoot
        {
            get
            {
                if (internalList is ICollection)
                    return ((ICollection) internalList).SyncRoot;
                else
                {
                    if (syncRoot == null)
                        Interlocked.CompareExchange(ref syncRoot, new object(), null);

                    return syncRoot;
                }
            }
        }

        bool ICollection.IsSynchronized
        {
            get
            {
                if (internalList is ICollection)
                    return ((ICollection)internalList).IsSynchronized;
                else
                    return false;
            }
        }

        public bool IsReadOnly
        {
            get { return internalList.IsReadOnly; }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            foreach (T t in internalList)
                yield return t;
        }

        public IEnumerator GetEnumerator()
        {
            return internalList.GetEnumerator();
        }
    }

Something you may want to add to these types of collections is enforcing a unique constraint when adding entities. You should probably use NHibernate's ISet interface if you want this kind of control, but this is an alternative if you want to use an IList bag.

public abstract class SetBehaviourListWrapper<T> : ListWrapper<T>
    {
        protected SetBehaviourListWrapper()
        {
        }

        protected SetBehaviourListWrapper(IList<T> internalList) : base(internalList)
        {
        }

        public new void Add(T instance)
        {
            if (Find(IsExistingUniqueKey(instance)) != null)
                throw new InvalidSetAdditionOperationException(InvalidSetAdditionOperationExceptionMessage(instance));

            base.Add(instance);
        }

        public new void AddRange(IEnumerable<T> instances)
        {
            foreach (T instance in instances)
                Add(instance);
        }

        public abstract Predicate<T> IsExistingUniqueKey(T instance);
        protected abstract string InvalidSetAdditionOperationExceptionMessage(T invalidInstance);
    }

You might then implement  a FooSet as:

public class FooSet : SetBehaviourListWrapper<Foo>
{
    public FooSet()
    {
    }

    public FooSet(IList<Foo> internalList) : base(internalList)
    {
    }

    public override Predicate<Foo> IsExistingUniqueKey(Foo foo)
    {
        return delegate(Foo f) { return f.Bar == foo.Bar; };
    }

    protected override string InvalidSetAdditionOperationExceptionMessage(Foo invalidInstance)
    {
        return string.Format("Key: Foo.Bar == {0}", invalidInstance.Bar);
    }
}

Site running on a free Atlassian Confluence Community License granted to Castle Project. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.4 Build:#809 Jun 12, 2007) - Bug/feature request - Contact Administrators