Generic DAO with NHibernate

4/28/06 Update: The NHibernate codeproject.com article wins ASP.NET article of the month. Woohoo!

3/14/06 Update: See how this is used in a sample application at http://www.codeproject.com/useritems/NHibernateBestPractices.asp.


Hibernate.org has a great article on creating a generic DAO for Hibernate in Java. Below is what I use for the C# port. 

The interface for common CRUD functionality...

public interface GenericDAO {

T GetById(IdDataType id, bool shouldLock);

List GetAll();

List GetByExample(T exampleInstance, string[] propertiesToExclude);

T SaveOrUpdate(T entity);

void Delete(T entity);

}

The generic DAO implementation...

public abstract class GenericNHibernateDAO : GenericDAO

{

/// Could be set using contruction injection IoC

public GenericNHibernateDAO(ISessionManager sessionManager) {

this.sessionManager = sessionManager;

}

public T GetById(IdDataType id, bool shouldLock) {

ISession session = GetSession();

T entity;

if (shouldLock) {

  entity = (T) session.Load(persitentType, id, LockMode.Upgrade);

}

else {

  entity = (T) session.Load(persitentType, id);

}

return entity;

}

public List GetAll() {

return GetByCriteria();

}

protected List GetByCriteria(params ICriterion[] criterion) {

ISession session = GetSession();

ICriteria criteria = session.CreateCriteria(persitentType);

foreach (ICriterion criterium in criterion) {

  criteria.Add(criterium);

}

GenericUtils genericUtils = new GenericUtils();

return genericUtils.ConvertToGenericList(criteria.List());

}

public List GetByExample(T exampleInstance, string[] propertiesToExclude) {

ISession session = GetSession();

ICriteria criteria = session.CreateCriteria(persitentType);

Example example = Example.Create(exampleInstance);

foreach (string propertyToExclude in propertiesToExclude) {

  example.ExcludeProperty(propertyToExclude);

}

criteria.Add(example);

GenericUtils genericUtils = new GenericUtils();

return genericUtils.ConvertToGenericList(criteria.List());

}

public T SaveOrUpdate(T entity) {

ISession session = GetSession();

session.SaveOrUpdate(entity);

return entity;

}

public void Delete(T entity) {

ISession session = GetSession();

session.Delete(entity);

}

private ISession GetSession() {

Check.Require(sessionManager != null, "sessionManager was not set");

return sessionManager.OpenSession();

}

private Type persitentType = typeof(T);

private ISessionManager sessionManager;

}

And for using this within your code...

public class ProjectDAONHibernate : GenericNHibernateDAO<Project, int> {

public ProjectDAONHibernate(ISessionManager sessionManager) : base(sessionManager) {}

}


Notes:

NHibernate Generics: To use generics with NHibernate, use the “NHibernate Generics” assembly available at http://www.ayende.com/projects/nhibernate-query-analyzer/generics.aspx.

SessionManager: To help with NHibernate session management, a good tool of choice is Castle Project's NHibernate facility:  http://www.castleproject.org/index.php/Facility:NHibernate.  Alternatively, a good design is explained in Chapter 8 of Hibernate In Action by Christian Bauer and Gavin King.  (They should be having a second edition coming out soon.)  The beneift of using Castle Project comes to light when used in conjunction with their Transaction factility; the Transaction facility allows you to mark which actions should be transactional via inline attributes.

Design by Contract:  You may have noticed Check.Require reference.  This is a completely optional, design-by-contract constraint using the VERY light weight framework described at http://www.codeproject.com/csharp/designbycontract.asp.

I've had wonderful success with NHibernate in the past and can continue to embrace it with C# 2.0 using generics.  The .NET community is finally starting to take notice of ORM tools like NHibernate which are helping to shave weeks, if not months, off of development.  I hope you all experience similar successes.