Sunday, December 30, 2007

NHibernate through C#

NHibernate through C#

Why NHibernate?
NHibernate takes care of low level data handling, and offers excellent supports multiple DBs.

Here is a sample application on the same
Create a simple table with the following structure
Id int
Title nvarchar(50)
EventDate datetime

Once done, create a new windows project and name it as “WindowsHibSample”. Create a new folder and name it “Entity”. Within this folder create a new class “Event” with the
following structure

Some Basic Requirements
1. Please note that all fields should be marked “private” and their accessors and mutators to be marked “public virtual”
2. You should define a public empty constructor. Feel free to use it the way you want it. Incase there is no need of it for you, even then define it. That’s the way NHibernate wants


public class Event
{
private int _id;
private string _title;
private DateTime _date;

public Event()
{
}
public virtual int Id
{
get { return _id; }
set { _id = value; }
}
public virtual string Title
{
get { return _title; }
set { _title = value; }
}
public virtual DateTime Date
{
get { return _date; }
set { _date = value; }
}
}


Once the basic class is defined, we should include reference for NHibernate application.

Library References
I’m sure you have humpty location that offer a download for the same. Download the latest version of it and add a reference of it to your project
I have used NHibernate that has a runtime version “v2.0.50727” and Version “1.2.1.4000”. Please ensure you have either this or versions above this in your system

Once you have added reference, Add App.config to your application. You App.Config should be similar to the one mentioned below


<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />
<section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<connectionStrings>
<add name="Connection String" connectionString="Database=SampleDb;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
</connectionStrings>
<nhibernate>
<add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
<add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2005Dialect" />
<add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
<add key="hibernate.connection.connection_string" value="Database=SampleDb;Server=(local)\SQLEXPRESS;Integrated Security=SSPI" />
<add key="hibernate.connection.pool_size" value="1"/>
<add key="hibernate.show_sql" value="true"/>
<!--
value="Server=localhost;initial catalog=nhibernate;Integrated Security=SSPI" />
-->
</nhibernate>
</configuration>




Application configuration will initialize NHiberante for the application when our application starts. Our next step is to bind Event object to be persisted in DB with Hibernate XML. This is achieved by creating a new xml file with the name Event.hbm.xml in the same folder as Event.cs. Please ensure the name of the xml file exactly matches name of the class (“Event” in this case)

Event.hbm.xml I used is appended

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="WindowsHibSample.Event, WindowsHibSample" table="Event">
<id name="Id" column="Id" type="Int32">
<generator class="assigned">
</id>
<property name="Title" column="Title" type="string" length="50">
<property name="Date" column="EventDate" type="DateTime">
</class>
</hibernate-mapping>


Once this is done, we are all set to write a program that wires Event.cs with Event.hbm.xml

For that to be achieved we should have a utility class for NHibernate that would open up a session, connection and close when all this is done

We could do this as such in every code that we write, but this could be better achieved by creating a utility class for this purpose. I have appended the class that I used


public class NHibernateUtil
{
private const string CurrentSessionKey = "nhibernate.current_session";
private static readonly ISessionFactory sessionFactory;
private static ISession currentSession;

static NHibernateUtil()
{
Configuration cfg = new Configuration();
cfg.AddAssembly("HibernateExamples");
sessionFactory = cfg.BuildSessionFactory();
currentSession = null;
}

public static ISession GetCurrentSession()
{
if (currentSession == null)
currentSession = sessionFactory.OpenSession();
return currentSession;
}

public static void CloseSession()
{
if (currentSession == null)
return;
currentSession.Close();
currentSession = null;
}

public static void CloseSessionFactory()
{
if (sessionFactory != null)
sessionFactory.Close();
}
}


We are all set to connect NHibernate with our application
I created a simple form with controls to accommodate/acquire EventId, name and date, added some validation to see if they in proper format etc.

I then created a simple DBHandler class that will handshake with NHibernateUtil class and pass on the object (Event) to be persited to NHibernate

Here is the sample code invoked on click of save button in my form


int eventId;
string eventTitle;
DateTime eventDate;

eventId = System.Convert.ToInt32(txtID.Text);
eventTitle = txtTitle.Text;
eventDate = System.Convert.ToDateTime(DateTime.Now);
EventPersistence ep = new EventPersistence(eventId, eventTitle, eventDate);
ep.SaveEvent();




EventPersistence class does the actual ground work of handshake with NHibernate. We could have all code of EventPersistence on button click itself, but that would make our application look really ugly, although you are free to do so, if you feel there are too many classes in the making


using System;
using System.Collections.Generic;
using System.Text;
using NHibernate;
using NHibernate.Cfg;
namespace WindowsHibSample
{
public class EventPersistence
{
private Event _evt;
public EventPersistence(int evtId, string evtTitle, DateTime evtDate)
{
_evt = new Event();
_evt.Date = evtDate;
_evt.Id = evtId;
_evt.Title = evtTitle;
}

public int SaveEvent()
{
if (_evt == null)
return -1;
Configuration cfg = new Configuration();
cfg.AddAssembly("WindowsHibSample");

ISessionFactory factory = cfg.BuildSessionFactory();
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();
//insert Record
session.Save(_evt);
// commit all of the changes to the DB and close the ISession
transaction.Commit();
session.Close();
return 0;
}
}




Our application is all set for execution. If you encounter any issues please feel free to login your comments or refer to the troubleshooting section below

I’ll add more examples on NHibernate shortly…..until then happy NHibernating……….

Troubleshooting
Mapping Exception was unhandled : Resource not found
Check to see if you have changed “Build Action Type” of .hbm.xml file to “Embedded Resource”

Mapping Exception was Unhandled: Could not compile the mapping document
If this is the error you receive, then you might have to do a little more of error tracking. Get complete error stack. Easiest way is execute the application, instead of debugging it and examine the complete details of the error. One of the major reason this shows up is mismatch of field names in the .hbm.xml file and fields in underlying class, this section is case-sensitive. If you have a field in you class as “Title“, but mapped in .hbm.xml as “title”, you will receive this error. To correct it ensure all fields and their mappings are right

NHibernate.DuplicateMappingException: Duplicate class/entity mapping Event
Specified class has already been loaded into memory and duplicate version of the same is being loaded explicitly/implicitly. Check for method configuration.AddClass(<>). NHibernate creates an instance of the class as and when it sees correct version .hbm.xml, and also loads it into memory, any effort to explicitly load will result in duplication

Could not save Object: Identifier Type Mismatch
Data types between C# object and DB don’t go well. Type mismatch includes even few simple changes like conversion from in to long and vice-versa. There should be no scope for type mismatch in our object definition to clear this error. More over this is ADOException hence debugging and identifying cause should be much easier, for people who have worked in ADO.

No comments: