views:

520

answers:

1

I'm using StructureMap as my IoC container and NHibernate as my ORM. I found an example online that shows how to have StructureMap build the ISessionFactory and the ISession so the Factory is a singleton and the Session is based on the HttpContext. This works great, but then I started using NH Profiler which told me I should always be explicitly using Transactions. So, I thought, why not let StructureMap handle that for me too? Using the code below, I got that all working fine, except, I don't know how/where to commit/rollback my transaction.

Here is how I initialize StructureMap:

    ObjectFactory.Initialize(x =>
 {
  x.ForRequestedType<ISessionFactory>()
   .CacheBy(InstanceScope.Singleton)
   .TheDefault.Is.ConstructedBy(cfg.BuildSessionFactory);

  x.ForRequestedType<ISession>()
   .CacheBy(InstanceScope.Hybrid)
   .TheDefault.Is.ConstructedBy(context => context.GetInstance<ISessionFactory>().OpenSession());

  x.ForRequestedType<ITransaction>()
   .CacheBy(InstanceScope.Hybrid)
   .TheDefault.Is.ConstructedBy(context => context.GetInstance<ISession>().BeginTransaction());

  x.Scan(y =>
  {
   y.TheCallingAssembly();
   y.WithDefaultConventions();
  });
 });

All my repositories look like the following:

 public UserRepository(ISession session, ITransaction transaction)
 {
  _session = session;
  _transaction = transaction;
 }

And a typical method inside a repository looks like:

 public void Remove(int id)
 {
  Remove(_session.Get<User>(id));
 }

What I'm trying to do is have all the methods that I call in one HttpContext share the same Session and Transaction. Is this possible or is this totally wrong and I'm barking up the wrong tree?

Thanks in advance!

-Dan

+1  A: 

This issue was why I created a simple UnitOfWork that combines ISession and ITransaction.

In tests, I would right the following code:

var product = new Product {Name = "Apple", Category = "Fruits"};
using (var session = _sessionFactory.OpenSession())
using (var transaction = _session.BeginTransaction())
{
    session.Save(product);
    transaction.Commit();
}

when I really wanted:

var product = new Product {Name = "Apple", Category = "Fruits"};
using (var unitOfWork = new UnitOfWork(_sessionFactory))
{
    unitOfWork.CurrentSession.Save(product);
    unitOfWork.Commit();
}

Here is my unit of work implementation,

using NHibernate;

namespace NHibernateBootstrap.Core.Persistence
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly ISessionFactory _sessionFactory;
        private readonly ITransaction _transaction;

        public UnitOfWork(ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
            CurrentSession = _sessionFactory.OpenSession();
            _transaction = CurrentSession.BeginTransaction();
        }

        public ISession CurrentSession { get; private set;}

        public void Dispose()
        {
            CurrentSession.Close();
            CurrentSession = null;
        }

        public void Commit()
        {
            _transaction.Commit();
        }
    }
}

As far as the when to call Commit() (either using ITransaction or UnitOfWork), I believe that it should be done explicitly before the end of your request. If you don't call it, then the UnitOfWork should clean itself up, but not commit. Alternatively, you could use an HttpModule to commit in an EndRequest handler.

wbinford