views:

979

answers:

2

I have a class Contact (base class),a class called Customer and a class called Supplier. Customer and supplier class both derive from Contact.

Customer has a 0..n relation with Order. I want to have a Collection property on customer and have it mapped in NHibernate to its corresponding table.

How is this done in NHibernate (version 2.0.1 GA) ?

(ps: using .NET 3.5 SP1, VS2008 SP1)

+3  A: 

This is done like this:

Create your class like this:

public class Customer  : Contact
{
   private ISet<Order> _orders = new HashedSet<Order>();

   public Collection<Order> Orders
   {
      return new List<Order>(_orders);
   }

   // NOrmally I would return a ReadOnlyCollection<T> instead of a Collection<T>
   // since I want to avoid that users add Orders directly to the collection.
   // If your relationship is bi-directional, then you have to set the other
   // end of the association as well, in order to hide this for the programmer
   // I always create add & remove methods (see below)

   public void AddOrder( Order o )
   {
      if( o != null && _orders.Contains(o) == false )
      {
         o.Customer = this;
         _orders.Add(o);
      }
   }
}

in your mapping, you specify this:

<set name="Orders" table="OrdersTable" access="field.camelcase-underscore" inverse="true">
   <key column="..." />
   <one-to-many class="Order" .. />
</set>

Since you use inheritance, you should definitly have a look at the different posibilities regarding inheritance-mapping in NHibernate, and choose the strategy that is best suited for your situation: inheritance mapping

Regarding set & bag semantics: - when you map a collection as a set, you can be sure that all the entities in the mapped collection, are unique. That is, NHibernate will make sure that, while reconstituting an instance, the collection will not contain duplicates. - when you map the collection as a bag, it is possible that your collection will contain the same entity more then once when loading the object from the DB.

  • A Set is a collection of distinct objects considered as a whole. A valid example of a set (of letters) is: { a, b, c, d }. Each letter occurs exactly once.
  • A Bag is a generalization of a set. A member of a bag can have more than one membership while each member of a set has only one membership. A valid example of a bag is { a, a, a, b, c, c, d, ...}. The letters a and c appear more than once in the Bag.
Frederik Gheysels
You were faster... Inheritance description here as well - http://ayende.com/Blog/archive/2009/04/10/nhibernate-mapping-ndash-inheritance.aspx
Rashack
I use Collection<Order> in my example... is that still possible ?
Patrick Peters
It is still possible, but in my example the private _orders member must be of type ISet, since it is mapped as a set.If you want _orders to be a List<Order> for instance, you'll have to map it as a bag. (But bag-mappings have other semantics then set-mappings).If you change the return type of the public Orders property to collection it is not a problem (in this example), since the public property is not used by NHibernate, but the private field is (hence the access-attribute in the mapping)
Frederik Gheysels
@Frederik: can you edit your answer to get it work with Collection<T>, including the right mapping file. Can you also describe a little more about the semantic-difference between bag- and set-mappings?
Patrick Peters
done; nothing changes to the mapping but see my edit.
Frederik Gheysels
@Frederik: The table attribute doesn't do anything if you have a one-to-many in the set. Either use table and composite-element OR use a reference to an entity that is mapped somewhere else using one-to-many. BTW, I wouldn't write a property that is creating a new list with every call to it.
Stefan Steinegger
@Frederik: it works! But.. I altered the code a little bit:private ISet<Order> _orders = new HashedSet<Order>();is changed to:private ICollection<Orderrow> orderrows = new Collection<Orderrow>();and the property will be:public Collection<Orderrow> Orderrows { get { return (Collection<Orderrow>)this.orderrows; } } and the mapping file:<bag name="orderrows" access="field.lowercase" inverse="true" lazy="false"> <key column="OrderId"/> <one-to-many class="Orderrow"/> </bag>
Patrick Peters
sorry, this is the code that works:private IList<Orderrow> orderrows = new List<Orderrow>();public Collection<Orderrow> Orderrows { get { return new Collection<Orderrow>(orderrows); } }
Patrick Peters
+1  A: 

Another solution if you don't like to use the set from Iesi collections

public class Customer  : Contact
{
   public ICollection<Order> Orders
   {
      get; private set;

   }
}

And the mapping like this:

<bag name="Orders" table="Customer_Orders" >
   <key column="Customer_FK" />
   <composite-element>
     <property name="OrderNumber" />
     <property name="OrderName" />
     <!-- ... -->
   </composite-element>
</set>
Stefan Steinegger
even better: http://feedproxy.google.com/~r/AyendeRahien/~3/etKiA3Njx-c/nhibernate-tidbit-ndash-using-ltsetgt-without-referencing-iesi.collections.aspx
Frederik Gheysels