nHibernate - XML & Fluent API

1.

As we saw in the earlier article Code First Entity Framework, Microsoft have given us an ORM solution which works out of the box. As an alternative to using theirs, an older ORM framework called nHibernate has been around for a few years. This is slightly more mature than EF, and is used in lots of places.

nHibernate comes in two flavours = XML configuration for mappings, and Fluent API. Let's start with the older version, which uses XML to map columns and classes.

2.

Let's create a table in the database:

CREATE TABLE [dbo].[tbl_contact](
	[contact_id] [bigint] IDENTITY(1,1) NOT NULL,
	[first_name] [varchar](255) NULL,
	[last_name] [varchar](255) NULL,
	[email] [varchar](255) NULL,
	[telephone] [varchar](50) NULL,
    CONSTRAINT [PK_tbl_contact] PRIMARY KEY CLUSTERED ( [contact_id] ASC )
) 

As you can see, this creates a table with a few customer related fields, with an Id primary key which is also an identity column generated by the database itself.

3. Now we need a POCO class in code which is to be represented in the database. If we choose Property names identical to the columns, then it makes life very easy for us, but for cussedness, lets use proper camelCase property names:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestnHibernate.Entity
{
    public class Contact
    {
        public virtual int ContactId { get; set; }

        public virtual string FirstName { get; set; }

        public virtual string LastName { get; set; }

        public virtual string Email { get; set; }

        public virtual string Telephone { get; set; }

        public  Contact()
        {
        }
    }
}
4.

So, if we want to use nHibernate to do our ORM, we need to ge the dlls from here. Unzip the download and add references to the binaries contained in it to your Contact project. For older versions of nHibernate, the core and fluent versions were in different dlls, but more modern versions (3.2 upwards) package all into a single nHibernate dll.

5.

XML Configuration - Add the following to the web.config of your app

  <configSections>
    <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
  </configSections>

  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
      <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
      <property name="connection.connection_string">Data Source=localhost;Initial Catalog=test;Integrated Security=True</property>
      <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
    </session-factory>
  </hibernate-configuration>
This lets .Net know to use nHibernate, and the connection string and adaptor type of the connection, and some other essential setup variables. You may need to download and reference Castle.Core if your version of nHibernate doesn't support it natively.
6.

Now, in the same place as you stored your Contact.cs file, create a small XML document called Contact.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="testnHibernate" assembly="testnHibernate">
  <class name="Contact" table="tbl_contact">
    <id       name="ContactId"  column="contact_id" type="int"><generator class="identity"></generator></id>
    <property name="FirstName"  column="first_name" type="String"/>
    <property name="LastName"   column="last_name"  type="String"/>
    <property name="Email"      column="email"      type="String"/>
    <property name="Telephone"  column="telephone"  type="String"/>
  </class>
</hibernate-mapping>
This file lets nHibernate know which table to map the Contact class to, and which column to map each Property to and the datatype. The auto-generated identity column is taken care of. This is all that's needed to configure these mappings in XML. Now to use it!
7. Now we need a form and a postback button click handler. I will leave the implementation of that to the student! Whether this is done as a ControllerAction in MVC or a button click handler in aspx, at some point you want to save your form values to the database using your new shiny nHibernate ORM.
        ...
      
        // setup nhibernate configuration
        NHibernate.Cfg.Configuration config = new NHibernate.Cfg.Configuration();
        config.AddAssembly(typeof(Contact).Assembly);
        NHibernate.ISessionFactory factory = config.BuildSessionFactory();
        NHibernate.ISession session = factory.OpenSession();
        NHibernate.ITransaction transaction = session.BeginTransaction();

        // create a contact from the form data (or if MVC, bound ViewModel properties or FormCollection)
        Contact contact = new Contact();
        contact.FirstName = txtFirstName.Text;
        contact.LastName = txtLastName.Text;
        contact.Email = txtEmail.Text;
        contact.Telephone = txtTelephone.Text;

        // tell NHibernate that this object should be saved and commit
        session.Save(contact);
        transaction.Commit();
        session.Close();

        ...
Obviously the session setup stuff can be done somewhere else, but all you do is open a session, create a new object, and save it to the session, then close the session. No actual database code is required, this is all that's needed to save to the database.
8. To retrieve a single entity by Id, or the entire list (by some criterion) you can use the following syntax

        // fetch a single contact from the database by id
        Contact singleContact = session.Get<Contact>(1);

        // fetch all contacts from the database 
        List<Contact> allContacts = (List<Contact>)session.CreateCriteria(typeof(Contact)).List<Contact>();

9. In recent years, everyone has been moving more towards using a Fluent API for configuring mappings, this makes mappings strongly typed, which removed runtime errors you get with text string configuration. So start a brand new project, and add the appropriate nHibernate dlls as references, and again add a class for the Contact object as before.

This time, there is no need for any configuration in web.config, and no mapping hbm files are required. Instead we create a mapping class in code:
    public class ContactFluentMap : ClassMap<Contact>
    {
        public ContactFluentMap()
        {
            Table("tbl_contact");
            Id(x => x.ContactId).Column("Contact_Id");
            Map(x => x.FirstName).Column("First_Name");
            Map(x => x.LastName).Column("Last_Name");
            Map(x => x.Email);
            Map(x => x.Telephone);
        }
    }
If the table happens to have the same name as the class, you don't need the table command. Likewise, we haven't had to put in column mappings for all of the Properties above, where the names are the same. Only where we cussedly made the database table columns have underscores do we need to be explicit.
10. Now in code, all we need to do is initialise the session object in code (NOT from from web.config!) and use the mapping file.
    private static ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("connectionStringKey")))
            .Mappings(m => m.FluentMappings.Add(typeof(ContactFluentMap)))
            .BuildSessionFactory();
    }
The only configuration info in the above is where it references the normal ConnectionStrings collection in web.config to know where to connect to your database. Here we define which mapping class to use, so we reference our new mapping class. Then in code, we have a static sessionFactory, which we use to save the data as we did in the XML example:
        var sessionFactory = CreateSessionFactory();
        using (var session = sessionFactory.OpenSession())
        {
            var transaction = session.BeginTransaction();
            // create contact
            Contact contact = new Contact();
            contact.FirstName = txtFirstName.Text;
            contact.LastName = txtLastName.Text;
            contact.Email = txtEmail.Text;
            contact.Telephone = txtTelephone.Text;
            session.Save(contact);
            transaction.Commit();
        }
The Get and CreateCriteria commands work in exactly the same way as before.
And that's the basics of nHibernate using both the XML configuration options and the Fluent API.