Eric Anastas
Eric Anastas

Reputation: 22223

How do I add items to a bi-directional one-to-many relationship in NHibernate?

Say I have the following classes Box and Item. Where a box contains many Items, and an Item can only be in one Box. An Item also knows what Box it is in.

public class Box
{ 
   public virtual int BoxID {get; set:}
   public virtual string Name {get; set;}
   public virtual IList<Item> Items {get; set;}
}

public class Item
{
    public virtual int ItemID {get; set;}
    public virtual string Name {get; set;}
    public virtual Box ContainerBox {get; set;}
 }

The Fluent NHibernate mapping classes might look like this.

public class BoxMap:ClassMap<Box>
{
    Id(b=>b.BoxId);
    Map(b=>b.Name);
    HasMany(b => b.Items)
        .Inverse()
        .CascadeAll();
}

public class ItemMap:ClassMap<Item>
{
    Id(i=>i.ItemId);
    Map(i=>i.Name);
    References(i => i.ContainerBox );
}

In general this works, but it's not as robust as I would like, as you need to know how associate things correctly. For example the Box.Items.Add() method will add items to the Box, but not update their ContainerBox properties. Like wise setting the Item.ContainerBox property does not add the item to the box.

So I figured I'd make Box.Items and IEnumerable to get rid of the Add() method, and instead add my own AddItem() method that will also set the Item.ContainerBox property.

public class Box
{ 
   private List<Item> _Items = new List<Item>();
   public virtual int BoxID {get; set:}
   public virtual string Name {get; set;}
   public virtual IEnumerable<Item> Items 
   {
       get{return _Items;}
       private set 
           {
               _Items.Clear();
               _Items.AddRange(value);
           }
   }

   public void AddItem(Item i)
   {
       if(i.ContainerBox != null) throw new Exception("Item is already in a box!");
       i.ContainerBox = this;
       _Items.Add(i);
   }
}

I'm new to NHibernate, but I'm pretty sure this is a bad idea. Because, when a Box is later persisted from the database Box.Items wont point at _Items but rather will be overridden to point at some other NHibernate specific proxy collection. So calling Box.AddItem() on the persisted box, I think, wont actually add a new Item to the Box.

What is the correct way to do this? I'd like to have a single way to add an Item to a Box which updates Box.Items and Item.ContainerBox regardless of whether the Box is new or persisted from the database.

Upvotes: 1

Views: 109

Answers (1)

Spencer Ruport
Spencer Ruport

Reputation: 35117

Change _Items to protected and set your fluent mapping to the _Items property and this will work just fine. I've used this technique in a number of ways.

Upvotes: 3

Related Questions