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.