NHibernate OneToMany surprises

Up to now, I did not care about NHibernate’s Sets and Bags, as my queries would target the details tables directly, and filter to the parent’s PK.

Now I tried to extend my Classes and ClassMaps generation code for Mapping.By.Code to Bags, and started out with something like this:

public partial class Foo
{
  public virtual int Id { get; protected set; }
  public virtual ICollection<Bar> Bars { get; set; }
}
public partial class Bar
{
  public virtual long Id { get; protected set; }
  public virtual Foo Foo { get; set; }
}

The mapping for Bar is not affected by the new Bars property:

public partial class Bar_Map: ClassMapping<Bar>
{
  public Bar_Map()
  {
    Table("Bar");
    Id(x => x.Id, map => ...);
    ManyToOne(x => x.Foo, map => ...);
  }
}

The master table gets a new collection property, depending on which NH mapping you use (SO):

Bag(x => x.Bars, bag =>
  {
    bag.Table("Bar");
    bag.Inverse(true);
    bag.Lazy(CollectionLazy.Lazy);
    bag.Cascade(Cascade.DeleteOrphans);
    bag.Key(k => {
      k.Column(col => col.Name("FooId"));  
          // this is the SQL column name
    });
  }, map => map.OneToMany());

So I try this code, retrieve a Foo, and count its Bars:

var foo = session.Get<Foo>(fooId);
Console.WriteLine(foo.Id);
Console.WriteLine(foo.Bars.Count());

Surprisingly, NH selects ALL Bar records into the collection, and counts the elements in the collection. That’s not what I expected.

On the NHibernate Pitfalls blog I found the hint to change the Lazy() setting to Lazy(CollectionLazy.Extra). And indeed, only a SELECT COUNT(*) was executed.

Somehow I was expecting the collection properties to be an alias for SELECT WHERE statements, so I tried things like foo.Bars.FirstOrDefault(), foo.Bars[0] (for IList) or foo.Bars.Take(1), but each of them always first populated the Bars collection in .Net, and only then retrieved the requested object from the collection, rather than issuing a separate SELECT.

For a complete list of documented surprises, see the NHibernate Pitfalls Index.

1 thought on “NHibernate OneToMany surprises

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.