Feeds:
Posts
Comments

Archive for the ‘NHibernate’ Category

Start with a one-to-many relationship in the database.

Customer has Orders

public class Customer
{
	public IList<Order> Orders { get; set; }
	// ...
}

public class Order
{
	public Customer Customer { get; set; }
	// ...
}

public class CustomerMap : ClassMap<Customer>
{
	public CustomerMap()
	{
		HasMany(x => x.Orders)
		.Cascade.All()
		.Inverse();
		// ...
	}
}

public class OrderMap : ClassMap<Order>
{
	public OrderMap()
	{
		References(x => x.Customer);
		// ...
	}
}

If we only call _customer.Orders.Add(order) the link between Order and Customer will not be bidirectional so NHibernate will not persist the Order and as a result when we reload the Customer the Order will not exist. We have to somehow link each Order to its Customer so that NHibernate can persist the relationship.

One popular solution to this problem, which is also the recommended solution in Java Persistence with Hibernate (p118), is to use a convenience method to add Orders so that we can simultaneously link them to the Customer.

public class Customer
{
	public IList<Order> Orders { get; set; }
	public void AddOrder(Order order)
	{
		// ...
		order.Customer = this;
		Orders.Add(order);
	}
}

This leads to bugs, however, when developers forget that they are supposed to use the convenience method. Which leads to a further refinement that prevents the developers from making that mistake.

public class Customer
{
	private IList<Order> _orders;
	public IEnumerable<Order> Orders 
	{ 
		get { return _orders; }
		private set { _orders = (IList<Order>)value; }
	}
	public void AddOrder(Order order)
	{
		// ...
		order.Customer = this;
		Orders.Add(order);
	}
}

This has two problems: 1) we have to cast the input to expose .Add() and 2) the developer could still cast the IEnumerable<Order> to IList<Order> to access the original list. We could resolve the second problem as follows:

public class Customer
{
	private IList<Order> _orders;
	public IEnumerable<Order> Orders 
	{ 
		get { return new ReadOnlyList(_orders ?? new List<Order>()); }
		private set { _orders = (IList<Order>)value; }
	}
	public void AddOrder(Order order)
	{
		// ...
        order.Customer = this;
		_orders.Add(order);
	}
}

But is it really a concern? Why is the developer doing that?

As for the first problem, a cleaner option is to have FluentNHibernate populate a private field so we don’t have to cast anything.

public class CustomerMap : ClassMap<Customer>
{
	public CustomerMap()
	{
		Component(x => x.Orders, m =>
		{
			m.HasMany<Order>(Reveal.Member<EntitySet<IOrder>>("_actual")).AsSet();
		});
	
		// ...
	}
	
	public class PersistentList<T>
		where T : class
	{
		private IList<T> _actual;
		public IEnumerable<Order> Orders { get{ return _orders;} }
		
		// ...
	}
}

Do we really want to use a magic string to bind to a private field? No, but the magic string can be eliminated if we use a mapping convention in FluentNHibernate (as Jimmy Board mentions in a comment on his posting):

public class CollectionAccessConvention : ICollectionConvention
{
	public void Apply(ICollectionInstance instance)
	{
		instance.Access.CamelCaseField(CamelCasePrefix.Underscore);
	}
}

which must then be configured in the FluentNHibernate SessionFactoryBuilder

public ISessionFactory Build()
{
	var sessionFactory = Fluently.Configure()
		.Database(_persistenceConfigurationBuilder.Build())
		.Mappings(mapping => mapping.FluentMappings.AddFromAssemblyOf<CustomerMap>()
		                     	.Conventions.Add<CollectionAccessConvention>()

// ...

So here we are. To prevent unintentional errors we add a convenience method then jump through hoops to force the developer to use the convenience method. Wait. Wait. Why are we having this arms race? It feels more like cutting the ends off the ham to me. Lets think about what we’re trying to accomplish here.

Why can’t we just create a custom collection that is coded to take care of the bidirecitonal mapping when Add (and Remove) are called?

Actually, we can, if we’re willing to tie the collection implementation to NHibernate by inheriting from NHibernate’s PersistentGenericBag<T>.

Another alternative along these lines is to make the custom collection implement NHibernate’s IUserCollectionType.

public class CustomerMap : ClassMap<Customer>
{
	public CustomerMap()
	{
		HasMany(x => x.Orders)
		.CollectionType(typeof(PersistentList<Order>))
		.Cascade.All()
		.Inverse();
		// ...
	}
	
	public class PersistentList<T> : IUserCollectionType
		where T : class
	{
		private IList<T> _actual;
		
		public void Add<T>(T item)
		{
			// ...
		}
		// ...
	}
}

But that requires us to implement several NHibernate specific methods in our collection.

What if we want the collection to be ignorant of NHibernate? One method is to configure NHibernate to use an interceptor to get/set the property.

public class CustomerMap : ClassMap<Customer>
{
	public CustomerMap()
	{
		HasMany(x => x.Orders)
			.Access.Using(typeof(PersistentListPropertyAccessor<Order>))
			.Cascade.All()
			.Inverse();
		// ...
	}
}

But then we have to implement a PersistentListPropertyAccessor based on this StackOverflow question to intercept the call to set the Orders list and wrap the input with a custom collection. When the collection is requested we intercept and unwrap the collection and return the original collection. This is all done through reflection however so there is a small performance hit. And if you want to abstract away configuring the parent to be set on the child more reflection may be required. Happy thoughts are gone.

Our Solution

We chose a hybrid solution that combines Jimmy’s collection convention with a custom collection as follows.

In our Customer class we have two fields for each collection. One that is used by NHibernate and a second one for our wrapper around the NHibernate collection.

public class Customer
{
	private IList<Order> _orders;
	private IPersistentList<Order> _wrappedOrders;

	public IList<Order> Orders
	{
		get { return _wrappedOrders ?? (_wrappedOrders = SetupBeforeAndAfterActions(_orders = _orders ?? new List<Order>())); }
	}

	private IPersistentList<T> SetupBeforeAndAfterActions<T>(IList<T> value) where T : class
	{
		return value.SetupBeforeAndAfterActions(x => x.Customer = this, x => x.Customer = null);
	}
}

So NHibernate gets direct access to a field it controls, we get to eliminate the AddX/RemoveX convenience methods from the parent, and the developer gets an IList<T> that knows how to set the parent or whatever else she needs it to do when items are added/removed. Everyone’s happy, so to speak, and the Nth implementation from the developer’s point of view is a one liner per property and one Setup method per class (as above).

Here are the wiring and Before/After extensions we use.

public static class IListTExtensions
{
	public static IPersistentList<T> SetupBeforeAndAfterActions<T>(
		this IList<T> value,
		Action<T> setParent,
		Action<T> setParentToNull)
		where T : class
	{
		var list = value as IPersistentList<T> ?? new PersistentList<T>(value);
		list.BeforeAdd = (l, x) => l.BeforeAddItem(x, setParent);
		list.BeforeRemove = (l, x) => { return l.BeforeRemoveItem(x, setParentToNull); };
		list.AfterAdd = AfterListChanges;
		list.AfterRemove = AfterListChanges;
		return list;
	}
	public static void AfterListChanges<T>(this IList<T> list) where T : class
	{
		// ...
	}

	public static bool BeforeAddItem<T>(this IList<T> list, T item, Action<T> setParent) where T : class
	{
		// ...
		setParent(item);
		if (list.Any(item.Equals))
		{
			return false;
		}
		return true;
	}

	public static bool BeforeRemoveItem<T>(this IList<T> list, T item, Action<T> setParentToNull) where T : class
	{
		// ...
		setParentToNull(item);
		// ...
	}
}

We ended up with the following generic custom collection. Most methods simply pass through to the same method on the contained list – but calls to BeforeAdd, AfterAdd, BeforeRemove and AfterRemove are injected where appropriate.

public interface IPersistentList<T> : IList<T>, ICollection
	where T : class
{
	Action<IList<T>> AfterAdd { get; set; }
	Action<IList<T>> AfterRemove { get; set; }
	Func<IList<T>, T, bool> BeforeAdd { get; set; }
	Func<IList<T>, T, bool> BeforeRemove { get; set; }
}

public class PersistentList<T> : IPersistentList<T>
	where T : class
{
	private readonly IList<T> _actual;
	private Action<IList<T>> _afterAdd;
	private Action<IList<T>> _afterRemove;
	private Func<IList<T>, T, bool> _beforeAdd;
	private Func<IList<T>, T, bool> _beforeRemove;

	public PersistentList(IList<T> actual)
	{
		_actual = actual;
	}

	/// <summary>
	///     perform actions on one or more list items after an item is
	///     added.
	/// </summary>
	public Action<IList<T>> AfterAdd
	{
		get { return _afterAdd ?? (_afterAdd = l => { }); }
		set { _afterAdd = value; }
	}

	/// <summary>
	///     perform actions on one or more list items after an item is
	///     removed.
	/// </summary>
	public Action<IList<T>> AfterRemove
	{
		get { return _afterRemove ?? (_afterRemove = l => { }); }
		set { _afterRemove = value; }
	}

	/// <summary>
	///     perform a check on the item being added before adding it.
	///     Return true if it should be added, false if it should not be 
	///     added.
	/// </summary>
	public Func<IList<T>, T, bool> BeforeAdd
	{
		get { return _beforeAdd ?? (_beforeAdd = (l, x) => true); }
		set { _beforeAdd = value; }
	}

	/// <summary>
	///     perform a check on the item being removed before removing
	///     it. Return true if it should be removed, false if it should not 
	///     be removed.
	/// </summary>
	public Func<IList<T>, T, bool> BeforeRemove
	{
		get { return _beforeRemove ?? (_beforeRemove = (l, x) => true); }
		set { _beforeRemove = value; }
	}

	public IEnumerator<T> GetEnumerator()
	{
		return _actual.GetEnumerator();
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}

	public void Add(T item)
	{
		if (BeforeAdd(this, item))
		{
			_actual.Add(item);
			AfterAdd(this);
		}
	}

	public void Clear()
	{
		while (_actual.Any())
		{
			RemoveAt(0);
		}
	}

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

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

	public bool Remove(T item)
	{
		if (BeforeRemove(this, item))
		{
			bool toReturn = _actual.Remove(item);
			AfterRemove(this);
			return toReturn;
		}
		return true;
	}

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

	void ICollection.CopyTo(Array array, int index)
	{
		var copy = new T[_actual.Count];
		_actual.CopyTo(copy, 0);
		Array.Copy(copy, 0, array, index, _actual.Count);
	}

	object ICollection.SyncRoot
	{
		get { throw new NotImplementedException(); }
	}

	bool ICollection.IsSynchronized
	{
		get { throw new NotImplementedException(); }
	}

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

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

	public void Insert(int index, T item)
	{
		if (BeforeAdd(this, item))
		{
			_actual.Insert(index, item);
			AfterAdd(this);
		}
	}

	public void RemoveAt(int index)
	{
		if (BeforeRemove(this, _actual[index]))
		{
			_actual.RemoveAt(index);
			AfterRemove(this);
		}
	}

	public T this[int index]
	{
		get { return _actual[index]; }
		set
		{
			// this is problematic because BeforeAdd has to act on the
			// new item and the existing list MINUS the item at index. 
			var copyWithoutItemAtIndex = new List<T>(_actual);
			copyWithoutItemAtIndex.RemoveAt(index);

			if (BeforeAdd(copyWithoutItemAtIndex, value))
			{
				_actual[index] = value;
				AfterAdd(this);
			}
		}
	}
}

Note: the custom collection also has to implement ICollection because ICollection<T> doesn’t implement ICollection

so you get an exception like the following when you try to save.

[ArgumentNullException: Collection cannot be null.
Parameter name: c]
   System.Collections.ArrayList..ctor(ICollection c) +10067700
   NHibernate.Collection.PersistentBag..ctor(ISessionImplementor session, ICollection coll) +101
   NHibernate.Collection.Generic.PersistentGenericBag`1..ctor(ISessionImplementor session, ICollection`1 coll) +49
   NHibernate.Type.GenericBagType`1.Wrap(ISessionImplementor session, Object collection) +158
   NHibernate.Event.Default.WrapVisitor.ProcessArrayOrNewCollection(Object collection, CollectionType collectionType) +330
   NHibernate.Event.Default.WrapVisitor.ProcessValue(Int32 i, Object[] values, IType[] types) +54
   NHibernate.Event.Default.AbstractVisitor.ProcessEntityPropertyValues(Object[] values, IType[] types) +74
   NHibernate.Event.Default.AbstractSaveEventListener.VisitCollectionsBeforeSave(Object entity, Object id, Object[] values, IType[] types, IEventSource source) +56
   NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess) +547
   NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess) +598
   NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) +50
   NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) +120
   NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) +333
   NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event) +162
   NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj) +145

This happens because the PersistentGenericBag implementation in NHibernate (2.1) makes the assumption that ICollection<T> inherits from ICollection, viz:

public PersistentGenericBag(ISessionImplementor session, ICollection<T> coll) 
	: base(session, coll as ICollection)
Advertisements

Read Full Post »

%d bloggers like this: