Rather than just start another argumentative post about whether stored procedures are better than using an ORM, I’m just going to give NHibernate a try for myself.  If you want a good breakdown of the argument, you can check out posts by Jeff Atwood, Eric Wise or Rob Howard.  If any of those articles don’t do it for you, just run a Google search on the topic…it has been debated about as much as the age old question of whether the chicken or the egg came first.

I have a secret  to admit…….I’ve never used an ORM before.  At my current job, we use stored procedures for all data access so I’ve never really had a reason to use them.  It’s always been one of those things on my “to do” list of stuff to check out that I never quite get to.  Today is the day to cross this one off the list! I’m going to approach this article differently than anything else I’ve written.  Usually I write about something I know or I’ve used, this time I’m going to write as I go.   I’m sure I’ll stub my toe along the way, and this will probably have to be broken up into multiple articles, but it should be an interesting journey.

I decided to try version 1.2.1 of NHibernate, even though I saw a 2.0 version available in alpha.  For a database, I wanted something with a lot of data in it that was available to anyone that actually wanted to follow along, so I decided to use everyone’s favorite database……….Adventureworks!

The first thing I want to do is create a class to represent some business entity.  I chose a simple example and created a Contact class, and I simply chose a few fields that exist in an AdventureWorks table called Person.Contact.  I’m starting simple because I’m learning as I go, so I’m not going to worry about how to include data in my class that would be populated via a joined query in a stored procedure.   I hope to get more complicated later, but I just want to get a feel for things first.

When I first created my class, I ran into an error that required me to make all of my public properties virtual.  When I made that change, my contact class ended up looking like this: 

   1:  public class Contact
   2:  {
   3:     private int _contactID;
   4:     private string _firstName;
   5:     private string _middleName;
   6:     private string _lastName;
   7:     private string _email;
   8:     private DateTime _lastModified;
   9:   
  10:     public virtual int ContactID
  11:     {
  12:        get { return _contactID; }
  13:        set { _contactID = value; }
  14:     }
  15:     public virtual string FirstName
  16:     {
  17:        get { return _firstName; }
  18:        set { _firstName = value; }
  19:     }
  20:     public virtual string MiddleName
  21:     {
  22:        get { return _middleName; }
  23:        set { _middleName = value; }
  24:     }
  25:     public virtual string LastName
  26:     {
  27:        get { return _lastName; }
  28:        set { _lastName = value; }
  29:     }
  30:     public virtual string Email
  31:     {
  32:        get { return _email; }
  33:        set { _email = value; }
  34:     }
  35:     public virtual DateTime LastModified
  36:     {
  37:        get { return _lastModified; }
  38:        set { _lastModified = value; }
  39:     }
  40:   
  41:     public Contact()
  42:     {
  43:   
  44:     }
  45:  }

The next thing I need to do is set up an XML file that maps my newly created class to the database table.  This is the crux of how NHibernate works, and it uses these mappings to load or change data in the database.  You will need one of these mapping files for every business entity that you are using.  I’m following instructions that I came across here when creating this file, and it ended up looking 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" table="Person.Contact">
   4:      <id name="ContactID" type="Int32">
   5:        <generator class="assigned" />
   6:      </id>
   7:      <property name="FirstName" type="String" length="50"/>
   8:      <property name="MiddleName" type="String" length="50"/>
   9:      <property name="LastName" type="String" length="50"/>
  10:      <property name="Email" column="EmailAddress" type="String" length="50"/>
  11:      <property name="LastModified" column="ModifiedDate" type="DateTime"/>
  12:    </class>
  13:  </hibernate-mapping>

I did run into a few snags when coming up with this mapping file.  The first is that the example I was reading used the xmlns of “urn:nhibernate-mapping-2.0″, and this was causing exceptions in the code to be thrown.  After some digging, I found that changing this to 2.2 fixed the problem. In case you run into the same problem, the exception message I got was “NHibernateSample.Contact.hbm.xml(2,2): XML validation error: Could not find schema information for the element ‘urn:nhibernate-mapping-2.0:hibernate-mapping’.“ 

In the XML file, you only have to specify the column if it differs from the property name, so in my example I needed to specify the column names for the Email and LastModified properties. I read that the type attribute is optional and that NHibernate will use reflection to figure it out if you leave it out, but it seems more efficient to me just to specify it now.  NHibernate needs to know what the primary key is for the table in question, and I specified this in the id section.  In my case, I told NHibernate that I would be assigning my own keys on line 5, where it says: generator class=”assigned”.  I also set the build action of the .xml file in my Visual Studio solution to be an embedded resource. 

The next step is to tell NHibernate how to get to my database.  I accomplished this by adding a section for NHibernate in my app.config file.  I believe you also have the option of creating a separate config file if you want rather than using the app.config (or web.config, depending on how you are using it) but I didn’t try it myself.  My app.config file ended up looking like this:

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:   
   4:    <configSections>
   5:      <section
   6:        name="nhibernate"
   7:        type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
   8:      />
   9:    </configSections>
  10:    
  11:    <nhibernate>
  12:      <add
  13:        key="hibernate.connection.provider"
  14:        value="NHibernate.Connection.DriverConnectionProvider"
  15:      />
  16:      <add
  17:        key="hibernate.dialect"
  18:        value="NHibernate.Dialect.MsSql2005Dialect"
  19:      />
  20:      <add
  21:        key="hibernate.connection.driver_class"
  22:        value="NHibernate.Driver.SqlClientDriver"
  23:      />
  24:      <add
  25:        key="hibernate.connection.connection_string"
  26:        value="Server=localhost;initial catalog=AdventureWorks;Integrated Security=SSPI"
  27:      />
  28:    </nhibernate>
  29:    
  30:  </configuration>

I’m sure there are lots of tuning options available here, but at this point I’m interested in getting it up and running.  I have to say, setting up all this configuration in XML files can be a little tedious!

At this point, I have everything configured correctly (I think) and I’m ready to actually try to pull some data out.  I guess the first thing I want to try is to pull a contact record out of the table based on the ID.  I put together this code to retrieve a contact by its ID, made my data layer look like this:

   1:  using NHibernate;
   2:  using NHibernate.Cfg;
   3:  using NHibernate.Expression;
   4:   
   5:  namespace NHibernateSample
   6:  {
   7:     public class DataLayer
   8:     {
   9:        public static Contact LoadContactById(int contactID)
  10:        {
  11:           Configuration cfg = LoadConfig();
  12:           ISessionFactory factory = cfg.BuildSessionFactory();
  13:           ISession session = factory.OpenSession();
  14:   
  15:           Contact current = (Contact)session.Load(typeof(Contact), contactID);
  16:   
  17:           return current;
  18:        }
  19:   
  20:        private static Configuration LoadConfig()
  21:        {
  22:           Configuration cfg = new Configuration();
  23:           cfg.AddAssembly("NHibernateSample");
  24:           
  25:           return cfg;
  26:        }
  27:   
  28:     }
  29:  }

 
What I’m doing is loading my configuration, creating an ISessionFactory from this configuration, and then loading my object based on the mappings I set up.   You can find some good examples of how this works here.  Lastly, to test all of this I set up a console application, and added a reference to my library to it.  The code in my console application just calls this function and prints out the results, it looks like this:

   1:  class Program
   2:  {
   3:     static void Main(string[] args)
   4:     {
   5:        Contact ct = DataLayer.LoadContactById(24);
   6:        StringBuilder sb = new StringBuilder();
   7:        sb.AppendLine("First Name: " + ct.FirstName);
   8:        sb.AppendLine("Middle Name: " + ct.MiddleName);
   9:        sb.AppendLine("Last Name: " + ct.LastName);
  10:        sb.AppendLine("Email: " + ct.Email);
  11:        Console.WriteLine(sb.ToString());
  12:   
  13:        Console.ReadLine();
  14:     }
  15:  }

To make sure this is working correctly, I took a peek in the table with SQL Management Studio and saw that with the ID I picked out, I should get a record back for the contact Sean P. Jacobson, and when I run that I see that this is in fact the case.

Success!  Granted, this was a simple example, but I was able to query data from the database without writing a line of SQL.  What happens if I want to change Sean’s last name?  By simply adding this method to my data layer I have full save capability. 

   1:  public static void SaveContact(Contact contact)
   2:  {
   3:     Configuration cfg = LoadConfig();
   4:     ISessionFactory factory = cfg.BuildSessionFactory();
   5:     ISession session = factory.OpenSession();
   6:     ITransaction transaction = session.BeginTransaction();
   7:   
   8:     Contact temp = (Contact)session.Load(typeof(Contact), contact.ContactID);
   9:     temp.FirstName = contact.FirstName;
  10:     temp.MiddleName = contact.MiddleName;
  11:     temp.LastName = contact.LastName;
  12:     temp.Email = contact.Email;
  13:   
  14:     session.Flush();
  15:     transaction.Commit();
  16:     session.Close();
  17:  }
 

To test this out, I added the code below to my console application after I had the contact record loaded and the update was successfully committed to the database.  I made this update an atomic operation using the ITransaction interface, and flushed the session to write the changes back to the database.

   1:  ct.MiddleName = "Humperdinkel";
   2:  DataLayer.SaveContact(ct);
 

The last thing I want to accomplish in my first NHibernate session is to load a group of contacts with a query.  I want to pass in a string, and load any contacts whose first name or last name contain those letters.  This is done using Expressions, which turned out to be fairly straight forward.  I added this class to my data layer:

   1:  public static IList ContactSearch(string search)
   2:  {
   3:     Configuration cfg = LoadConfig();
   4:     ISessionFactory factory = cfg.BuildSessionFactory();
   5:     ISession session = factory.OpenSession();
   6:   
   7:     IList contacts = session.CreateCriteria(typeof(Contact))
   8:        .Add(Expression.Or(Expression.Like("LastName", "%" + search + "%"),
   9:                       Expression.Like("FirstName", "%" + search + "%")))
  10:        .List();
  11:   
  12:     return contacts;
  13:  }

Then by adding this block of code to my console application, I was able to query for a list of any contact in the database that had the text ‘Joe’ in their first name or last name.

   1:  IList contacts = DataLayer.ContactSearch("Joe");
   2:   
   3:  if (contacts.Count == 0)
   4:     Console.WriteLine("No contacts match.");
   5:  else
   6:  {
   7:     Console.WriteLine(contacts.Count.ToString() + " matching contact(s):");
   8:     foreach (Contact current in contacts)
   9:        Console.WriteLine(current.FirstName + " " + current.LastName);
  10:  }

Running the console app prints a list of 67 names, which is the same number I got back when I wrote the SQL by hand to test things out.   I was successfully able to use NHibernate to query the AdventureWorks database, and I was able to save data back to the database…..all without writing a line of SQL. 

In my next article, I will try to get a little more complex and explore relationships.  Up until now, my examples have been simple….the business objects got their data from a single table.  I want to expand this to capture data for a business entity from multiple tables.  I will eventually dig into how Castle’s mapping pattern project ActiveRecord works with NHibernate, and I will determine if we can get rid of some of the XML configuration pain I ran into.

kick it on DotNetKicks.com