Getting Started with NHibernate

by October 14, 2009 11:40 PM

I gave a presentation to our internal user group at work today (devloop). The topic of the day was "Getting Started with NHibernate". I'd say it went alright.. there was plenty of interest and several people wanted the code afterward. Good sign I suppose. However, I thought I did a crappy job.

  • I didn't prepare hardly at all (though I was given very little notice)
  • I felt chaotic and rushed the whole time (common result of poor prep)
  • I did some things for the sake of time that I would never prescribe on a real project
  • I did some things that added too much noise to the topic at hand (MVC, repository, and unit of work for starters)

So then, I don't think I'll share today's mayhem that resembled C#. Instead I took some time this evening to put together my first screencast. This presentation goes way back to basics and focuses on nothing but getting going with nhibernate, linq to nhibernate, and fluent nhibernate. No patterns. No best practices. Just the absolute basics on getting going from scratch.

The code from the screencast: 2009-10-14_NHDemo.zip
The screencast: 2009-10-14_nhibernate.wmv (this seems to have gotten deleted!)

And finally, in case you're wondering...

All in all I had fun putting it together. I am quite sure there are more efficient ways of going about it though :)

Enjoy!

Tags: , , ,

NWA DNUG - Linq to NHibernate

by September 16, 2009 10:09 PM

I just wanted to post the code from last night's NWADNUG lightning round for those who asked.

LinqToNHibernate.zip

A group of us were also talking about repositories at Jose's after the meeting. I wanted to remind of you this post where I showed the repository I use on one of my projects at work. It's another case where I'm not using an IoC container to manage the unit of work.

Feel free to fire away with any questions!

Happy coding

Tags: , , ,

Linq to NHibernate Repository

by August 31, 2009 09:50 AM

A colleague at work asked for some guidance on creating a generic repository that uses Linq to NHibernate and I thought I'd reply here instead of directly in case anyone else might find the information useful. First thing first, here is the repository I use on a project at work. I'll talk to interesting pieces of it below the code.

public interface IRepository
{
ISession NHSsession { get; }
/// <summary>
/// Loads a proxy object with nothing but the primary key set.  
/// Other properties will be pulled from the DB the first time they are accessed.
/// Generally only use when you know you will NOT be wanting the other properties though.
/// </summary>
T Load<T>(object primaryKey);
T Get<T>(object primaryKey);
T Get<T>(Expression<Func<T, bool>> predicate);
IQueryable<T> Find<T>();
IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate);
T Add<T>(T entity);
T Remove<T>(T entity);
}
public class Repository : IRepository
{
static Repository()
{
_sessionFactory = Fluently.Configure()
.Database(OracleDataClientConfiguration
.Oracle9
.ConnectionString(c => c.FromConnectionStringWithKey("CPSDsn"))
.Driver("NHibernate.Driver.OracleClientDriver")
.ShowSql()
)
.Mappings(mapping => mapping.FluentMappings.AddFromAssemblyOf<Repository>())
.ExposeConfiguration(config => config.SetInterceptor(new AppInterceptor()))
.BuildSessionFactory();
}
private static readonly ISessionFactory _sessionFactory;
private static ISession _testingSession;
public static ISession NHSession
{
get { return HttpContext.Current == null ? _testingSession : HttpContext.Current.Items["_nhSession"] as ISession; }
set
{
if (HttpContext.Current == null)
_testingSession = value;
else
HttpContext.Current.Items["_nhSession"] = value;
}
}
public static void BeginUnitOfWork()
{
if (NHSession != null)
throw new ApplicationException("Unit of Work already started");
NHSession = _sessionFactory.OpenSession();
NHSession.FlushMode = FlushMode.Commit;
NHSession.BeginTransaction();
}
public static void EndUnitOfWork()
{
if (NHSession == null) return;
NHSession.Transaction.Rollback();
NHSession.Dispose();
NHSession = null;
}
public static void SubmitChanges()
{
try
{
NHSession.Transaction.Commit();
NHSession.BeginTransaction();
}
catch
{
NHSession.Transaction.Rollback();
throw;
}
}
public static void CloseSessionFactory()
{
_sessionFactory.Dispose();
}
/*******************************************************************************/
/*******************************************************************************/
public Repository()
{
_session = NHSession;
}
private readonly ISession _session;
public ISession NHSsession { get { return _session; } }
public T Load<T>(object primaryKey)
{
return _session.Load<T>(primaryKey);
}
public T Get<T>(object primaryKey)
{
return _session.Get<T>(primaryKey);
}
public T Get<T>(Expression<Func<T, bool>> predicate)
{
return Find<T>().SingleOrDefault(predicate);
}
public IQueryable<T> Find<T>()
{
return _session.Linq<T>();
}
public IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate)
{
return Find<T>().Where(predicate);
}
public T Add<T>(T entity)
{
_session.Save(entity);
return entity;
}
public T Remove<T>(T entity)
{
_session.Delete(entity);
return entity;
}
}

If I were looking at this for the first time, I think these things would jump out at me:

  • Static constructor - we're not using an IoC container to manage the NH session or session factory, so I saw this as the best way to reliably initialize the session factory. Remember that static constructors are guaranteed to be thread safe and are only called once. Pretty much exactly what we need for the NH session factory.
  • All the other static members - since we're not managing the NH session with IoC either, I needed a way to get a session. Also since this is from a web application I wanted the unit of work to be per http request. So in the Global.asax events BeginRequest and EndRequest, I call Repository.BeginUnitOfWork() and Repository.EndUnitOfWork(). And of course I didn't want any unexpected DB changes so you have to explicitly tell the repository to submit changes otherwise everything gets rolled back when ending the unit of work. The only thing remaining is the call to close the session factory. This is only used in unit tests.
  • All the NH specific stuff - yes, yes, I know. This isn't a repository you could plug into any ORM solution. I used to balk at such things that were implementation specific, but at some point I realized I was missing out on benefits of that chosen implementation. In fact, I'll talk to those benefits next.

Having talked to those things that JUMP out at you at first glance, the rest is pretty straight forward. The repository gives you what you need to get entities, get lists of entities, and of course add and remove entities from the DB.

The two methods that are specific to NH (I think) are the Load and Get methods (the Get that takes an object as primary key). Here are the reasons for their existence.

  • T Get<T>(object primaryKey) - if you use NHibernate's Get method, you'll get the added benefit of knowing NH might not have to go to the DB for that entity. If NH already has that entity loaded due to some previous call, then it will just return the one it already has. That's just freaking cool! If however, you use the other Get method that takes a predicate and uses the Linq method SingleOrDefault to get the entity, you'll hit the DB every time even if you're passing in the same predicate every time. Not cool.
  • T Load<T>(object primaryKey) - this one is very cool. If NH already has the requested object in memory, it will return to you the real deal. If not however, NH does not got to the DB to get it. Instead a proxy object is returned with nothing but the primary key set. You can use that object just as you would the real thing (pass it to constructors, use it as a parameter, etc). The intention is to use in situations where a reference to the entity is needed, but only for the sake of the relationship (FK in the DB usually), or to get to the primary key value. As a simple example, here is the body of one of the remove methods on one of my repositories: _repository.Remove(_repository.Load<MinorLine>(minorLineId));

I think that's about it. I hope you find it useful or that it at least sparks ideas for your own repository implementation.

Tags: , , ,