Today I hit a quite subtle bug setting up persistence for our soon-to-appear new todo management tool. Since we’re building on ASP.NET 4 I simply used the ISet<T> and HashSet<T> types in the System.Collections.Generic namespace for all entity collections, e.g.
1: using System.Collections.Generic;
2: namespace Teamaton.TodoCore.Entities
3: {
4: public class Group
5: {
6: public ISet<Person> Members { get; set; }
7: public string Name { get; set; }
8: }
9: }
But when I called _session.Save(entity) I got the following error:
At first I thought: oh, yeah, of course! I didn’t initialize my Members property! No problem.
So I added a simple constructor to initialize all my ISet<T> properties like this:
1: public Group()
2: {
3: Members = new HashSet<Person>();
4: }
I still go the same error. Well, there were quite a few of these collections and I went and added constructors of the same kind everywhere.
Ran the code again – still the same ol’ error! Arrrrg!
Ok, Google to the rescue: http://www.google.com/search?q=nhibernate+System.ArgumentNullException%3A+Collection+cannot+be+null lead me to NHibernate custom collection options, an interesting read although not really to the point. But wait: there, towards the end, I finally found the essential piece of information I was missing:
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
Well, the same is true for ISet<T>: it does not implement ICollection! So when NHibernate performs its internal collection magic and is passed a reference to a collection of type HashSet<T> which implements ISet<T> and even ICollection<T> it throws an error because it really expects an ICollection. And then I remembered, that I’ve tried using the System.Collections.Generic classes with NHibernate before and it didn’t work because it internally uses the Iesi.Collections.
So I had to change my HashSet<T> to HashedSet<T>, reference the Iesi.Collections.dll and all was well! ISet<T> is defined in Iesi.Collections as well, so I didn’t have to change that.
Happy Coding!