Reconsituting Domain Collections with NHibernate

We ran into a problem using NHibernate to persist our domain. Here's an example of a domain object; an Order class with a collection of OrderLine objects to represent the line in each order placed in the system. In the system we want to be able to check if an order exists or not so we use an anonymous delegate as a predicate on the OrderLine collection:

    7 class Order

    8 {

    9     private IList<OrderLine> Lines;

   10 

   11     public Order()

   12     {

   13         Lines = new List<OrderLine>();

   14     }

   15 

   16     public bool DoesOrderExist(string OrderNumber)

   17     {

   18         return ((List<OrderLine>)Lines).Exists(

   19             delegate(OrderLine line)

   20                 {

   21                     if (line.OrderNumber == OrderNumber)

   22                         return true;

   23                     return false;

   24                 }

   25             );

   26     }

   27 }

   28 

   29 class OrderLine

   30 {

   31     public string OrderNumber;

   32     public int Quantity;

   33     public string Item;

   34     public double Cost;

   35 }

This is all fine and dandy but when we reconsistute the object from the back-end data store using NHibernate, it blows its head off with an exception saying it can't cast the collection to a list. Internally NHibernate creates a PersistantBag object (which implements IList) but can't be directly cast to a List, so we can't use our predicate.

There's a quick fix we came up with which is to modify the DoesOrderExist method to look like this instead:

   16 public bool DoesOrderExist(string OrderNumber)

   17 {

   18     List<OrderLine> list = new List<OrderLine>(Lines);

   19     return (list.Exists(

   20         delegate(OrderLine line)

   21             {

   22                 if (line.OrderNumber == OrderNumber)

   23                     return true;

   24                 return false;

   25             }

   26         );

   27 }

This feel dirty and smells like a hack to me. Rebuilding the list from the original one when we want to find something? Sure, we could do this and cache it (so we're not recreating it every time) but that just seems ugly.

Any other ideas about how to keep our predicates intact when reconsituting collections?

8 Comments

  • Ain't POCO fun :)

    I really wonder why you don't use a simple for loop, as it's less lines and better readable and has at least the same performance.

  • I suppose you could collapse the predicate to delegate(OrderLine line) { return line.OrderNumber == OrderNumber; } or delegate(OrderLine line) { return line.OrderNumber.CompareTo(OrderNumber); } which would cut down on the code but yeah, a simple for loop would work here.

  • Another possibility is to re-introduce repositories, as these kind of methods should perhaps be in a repository instead of the domain object / aggregate.

  • Should they? In this case we're talking about something theoretical, but in a real domain it would have a set of objects that are really part of it (in our case we have a cable with segments).

    It doesn't seem to make sense to create an entire repository just to hand out segments from a list when they're really part of the Cable.

  • I feel your pain. In this specific scenario, I'm with Frans - just "foreach" it.

  • A for / foreach would be better here.
    Another option is to use a helper method that accept a collection and a delegate, and calls the delegate on each item.

  • I'll recommend the foreach as well. Anonymous delegates are great for readability and conciseness (is that a word?)... if the syntax is good. Unfortunately in 2.0 it's just ugly. You could also wrap up the anonymous delegate as a Specification object if you want to test it separately. Then you could call list.Exists(spec.IsSatisfiedBy).

  • Just taking a different route for the sake of it (I don't actually think doing that cast is a 'Good Thing' anyway), but...
    Can't you make NHibernate map that association using a List ? Perhaps involves explicitly avoiding lazy loading ?

Comments have been disabled for this content.