Handling Collections with NHibernate

When business entities hold some collection and you read the NHibernate manual on how to handle this, most parts of the book (not all) will either tell you to expose this either as an IList (more pragmatic) or as an ISet (more beautiful, performant, failsafe, etc.).

I am under the impression that it generally is a bad idea to expose any type of collection that way. Why?

(I go with IList here, but same applies to any other collection)

case 1)

class Bar {
    public Foo Parent {get;set;}
}

class Foo {
    public IList<Bar> Bars {get; set;}
}

Now you are completely exposed. Anyone who has to handle this code, must be conscious of any side effects this may have. For instance, if you have a bidirectional relation to handle you must code like that, or you’ll get an error:

var bar = new Bar();
var foo = new Foo();
bar.Foo = foo;
foo.Bars = new List<Bar>();
foo.Bars.Add(bar);

Quite a bit of code that will repeat over and over in your app. My verdict: avoid this! Many people go with something slightly better:

2) Shielded and somewhat correct

class Foo {
    IList bars<Bar> = new List<Bar>();

    public IList<Bar> Bars {
        get { return new List<Bar>(bars).AsReadonly; }
        private set { bars = value; }
    }

    public void AddBar(Bar bar){
        // set whatever is necessary to keep consistence and let the user forget
        // about persistence details like bidirectionality
    }
}

If you unit test this, your tests will be green and you will be shielded against mistakes. You can forget the inner workings and everything will be fine. That is, unless you want to load this collection lazily (and receive lots of performance benefits). I actually already forgot what the concrete problem was, but it seems, that NHibernate inspects, what Bars delivered and tries to give the same back using the private setter. It got a ReadOnlyCollection, so it tries to set one. And this fails somehow. (I’ll try to come up with the original error). So you either return bars directly and leave two possible ways to access it, one that works, and one that sometimes works. Horror. Or. you go, by what I do right now.

3) Shielded

class Foo {
    private ISet<Bar> barSet;

    public ReadOnlyCollection<Bar> Bars {
        get {return new List<Bar>(barSet); }
    }

    public void AddBar()Bar bar {
        // add a bar
    }
}

This

  1. uses the NHibernatewise better performing Set, that most possibly also represents much better your idea of what should be returned.
  2. returns a shielded IList, that has the foreach semantics you were looking for (ISet has not)
  3. Shields you from future mistakes

Mind: You map against bars using a <set/> tag. This is a very basic pattern, but I thought, if you are new to it, it might give you a better start. I did not find this documented anywhere.

Leave a Reply