In my last article, I decided to give NHibernate a try to try and get my mind around what an ORM brings to the table.  I took a few hours and managed to get a simple example working, and was able to read and write data to the AdventureWorks database.   My example was simple though, now I would like to try and dig a little deeper into NHibernate.

The first topic that I would like to delve into is relationships.  In my last article, I had a Contact business entity, but I was pulling all of the data for this object from a single database table.  This is often not the case in the real world, especially in situations where you don’t have absolute control over the database schema.  I would like to expand my example and add a new class called Employee that is derived from the Contact class.  I’ll just pick a few fields from the HumanResources.Employee table in AdventureWorks for this class, making my class diagram look like this:

If I were writing a stored procedure this would be a walk in the park for me, I would just join the relevant tables in the database with an inner join and populate the Employee record with the results.  The SQL query would look something like this:

select e.EmployeeID, e.Title, e.HireDate, e.SalariedFlag, c.FirstName, 
c.MiddleName, c.LastName, c.EmailAddress, c.ModifiedDate
from HumanResources.Employee e
inner join Person.Contact c on e.ContactID = c.ContactID
where e.ContactID = @ID

Let’s figure out how to do the same task using NHibernate.  I’ll admit, it took me some trial and error to find a way to do this.  The learning curve to pick up all of the nuances for class mapping is a little steep.  I started by trying to create a mapping file for my Employee class, but I was hitting a wall because of how I set up the inheritance.  I’m fairly sure I could have gotten it to work that way if I had the Employee class encapsulate the Contact class rather than inheriting from it.  I ended up getting this to work by editing the mapping file for the Contact class, which was a little non-intuitive to me.  My new mapping file looks like this:

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
   3:    <class name="NHibernateSample.Contact, NHibernateSample" 
   4:                                                               table="Person.Contact">
   5:      <id name="ContactID" type="Int32">
   6:        <generator class="assigned" />
   7:      </id>
   8:      <property name="FirstName" type="String" length="50"/>
   9:      <property name="MiddleName" type="String" length="50"/>
  10:      <property name="LastName" type="String" length="50"/>
  11:      <property name="Email" column="EmailAddress" type="String" length="50"/>
  12:      <property name="LastModified" column="ModifiedDate" type="DateTime"/>
  13:   
  14:      <joined-subclass name="NHibernateSample.Employee,
  15:         NHibernateSample" table="HumanResources.Employee">
  16:        <key column="ContactID"/>
  17:        <property name="EmployeeID" type="Int32"/>
  18:        <property name="Title" type="String" length="50"/>
  19:        <property name="HireDate" type="DateTime"/>
  20:        <property name="SalaryFlag" column="SalariedFlag" type="Boolean"/>
  21:        <property name="LastModified" column="ModifiedDate" type="DateTime"/>
  22:      </joined-subclass>
  23:      
  24:    </class>
  25:  </hibernate-mapping>

I added the joined-subclass section to the mapping file, which tells NHibernate how to load the information that is specific to the employee.  I loaded a random employee from the database (ContactID = 1001) and I was able to return the correct data.

I was worried that this solution would break previous examples.  In the AdventureWorks schema, not all contacts are employees, which is consistent with our data model.  Since I modified the mapping file for the Contact class, I thought I might have broken things in the situation where I load a Contact record that wasn’t actually an employee.  NHibernate handled this situation perfectly, and I was able to load the contact record successfully.

Next on my list of things to try was to get a one to many relationship to work.  The logical place for me to test this out is in the ordering system.  I want create an Order class, and in this class I want to store a list of order details that make up the order.  To add a little more complication, I made the OrderDetail class encapsulate a Product class, making my final class structure look like this:

   1:  public class Order
   2:  {
   3:     private int _orderID;
   4:     private DateTime _orderDate;
   5:     private decimal _orderTotal;
   6:     private IList _lineItems;
   7:   
   8:     public virtual int OrderID
   9:     {
  10:        get { return _orderID; }
  11:        set { _orderID = value; }
  12:     }
  13:     public virtual DateTime OrderDate
  14:     {
  15:        get { return _orderDate; }
  16:        set { _orderDate = value; }
  17:     }
  18:     public virtual decimal OrderTotal
  19:     {
  20:        get { return _orderTotal; }
  21:        set { _orderTotal = value; }
  22:     }
  23:     public virtual IList LineItems
  24:     {
  25:        get { return _lineItems; }
  26:        set { _lineItems = value; }
  27:     }
  28:   
  29:     public Order()
  30:     {
  31:   
  32:     }
  33:  }
  34:   
  35:  public class OrderDetail
  36:  {
  37:     private int _orderDetailID;
  38:     private int _quantity;
  39:     private string _trackingNumber;
  40:     private decimal _price;
  41:     private Product _item;
  42:   
  43:     public virtual int OrderDetailID
  44:     {
  45:        get { return _orderDetailID; }
  46:        set { _orderDetailID = value; }
  47:     }
  48:     public virtual int Quantity
  49:     {
  50:        get { return _quantity; }
  51:        set { _quantity = value; }
  52:     }
  53:     public virtual string TrackingNumber
  54:     {
  55:        get { return _trackingNumber; }
  56:        set { _trackingNumber = value; }
  57:     }
  58:     public virtual decimal Price
  59:     {
  60:        get { return _price; }
  61:        set { _price = value; }
  62:     }
  63:     public virtual Product Item
  64:     {
  65:        get { return _item; }
  66:        set { _item = value; }
  67:     }
  68:   
  69:     public OrderDetail()
  70:     {
  71:   
  72:     }
  73:  }
  74:   
  75:  public class Product
  76:  {
  77:     private int _productID;
  78:     private string _name;
  79:     private string _productNumber;
  80:   
  81:     public virtual int ProductID
  82:     {
  83:        get { return _productID; }
  84:        set { _productID = value; }
  85:     }
  86:     public virtual string Name
  87:     {
  88:        get { return _name; }
  89:        set { _name = value; }
  90:     }
  91:     public virtual string ProductNumber
  92:     {
  93:        get { return _productNumber; }
  94:        set { _productNumber = value; }
  95:     }
  96:   
  97:     public Product()
  98:     {
  99:   
 100:     }
 101:  }

You’ll notice on lines 6 and 23 above that my collection of OrderDetail objects is stored as an IList.  Normally I would use a List<OrderDetail> in this situation, but I couldn’t get NHibernate to map this correctly, so I used an IList instead.  The next step is to come up with the NHibernate mapping files.  A little reminder, make sure you change your .hbm.xml files to be embedded resources in the build action in Visual Studio. I forgot to do this and NHIbernate throws an exception and will be unable to load the class.  I created a mapping file for each of my 3 new classes, and they look like this:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="NHibernateSample.Order, NHibernateSample"
                                table="Sales.SalesOrderHeader">
    <id name="OrderID" column="SalesOrderID" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="OrderDate" type="DateTime"/>
    <property name="OrderTotal" column="TotalDue"
                               type="Decimal"/>

    <bag name="LineItems" inverse="true">
      <key column="SalesOrderID"/>
      <one-to-many
           class="NHibernateSample.OrderDetail, NHibernateSample"/>
    </bag>

  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="NHibernateSample.OrderDetail, NHibernateSample"
                                  table="Sales.SalesOrderDetail">
    <id name="OrderDetailID" column="SalesOrderDetailID"  type="Int32">
      <generator class="assigned" />
    </id>
    <property name="Quantity" column="OrderQty"  type="Int32"/>
    <property name="TrackingNumber" column="CarrierTrackingNumber"
                                         type="String" length="25"/>
    <property name="Price" column="UnitPrice" type="Decimal"/>

    <one-to-one name="Item"
            class="NHibernateSample.Product, NHibernateSample"/>

  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="NHibernateSample.Product, NHibernateSample"
                      table="Production.Product">
    <id name="ProductID" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="Name" type="String" length="50"/>
    <property name="ProductNumber" type="String" length="25"/>
  </class>
</hibernate-mapping>

There are several new additions to these mapping files. First, in the Order class, I have a bag construct, which specifies a one to many relationship to OrderDetail.  A bag means that I have a collection of objects, and is mapped to the IList in my class definition.  The one-to-many construct also makes sense, because 1 order has many line items.  In the OrderDetail class, we have a one-to-one relationship defined to the Product class.  This is also intuitive when you think about it, because a single line item represents 1 product.  The quantity purchased can be more than one, but we’re still talking about a single product.

To test this out, I created a method in my data layer to load an order based on the order ID which looks like this:

   1:  public static Order LoadOrderById(int orderID)
   2:  {
   3:     Configuration cfg = LoadConfig();
   4:     ISessionFactory factory = cfg.BuildSessionFactory();
   5:     ISession session = factory.OpenSession();
   6:   
   7:     Order current = (Order)session.Load(typeof(Order), orderID);
   8:   
   9:     return current;
  10:  }

I plugged in a valid order ID from the database, and everything loaded perfectly! I’m starting to see the power of NHIbernate, although mapping relationships can be a little tough to grasp at first.  When learning how to map classes, I found the NHibernate site to be an invaluable resource, so be sure to check it out.  Next time I will take ActiveRecord for a spin and see if it makes the process of mapping classes any easier.

kick it on DotNetKicks.com