getit
getit

Reputation: 621

NHibernate transactions within UnitOfWork Sessions

The project I am working on has a UnitOfWork defined for an entire session (as seems to be standard practice with MVC + NHibernate sites)

WHat I need to do though, is be able to loop over a collection of items and prcess them one by one in their own "local" transaction.

something like this:

foreach(var item in CollectionOfItems)
 {
      using (ITransaction transaction = UnitOfWork.CurrentSession.BeginTransaction())
      {
         //do work on item. rollback on failure, 
         //but it should not affect the other items
      }
 }

But this won't work because the BeginTransaction line is using the same "outer" session. How can I get a self-contained "local" session to perform a transaction on a small block of code? I think the session is being injected into the unit of work in the following code. I don't know exactly how, though:

The UnitOfWOrk class has the following constructor

    private readonly ISessionFactory _sessionFactory;
    private readonly ITransaction _transaction;

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

It is registered via:

For<IUnitOfWork>().LifecycleIs(new HybridLifecycle())
            .Use<UnitOfWork>();

So whenever a controller returns a response, the session gets flushed. This is where I am confused. I don't always want everything to be all or nothing.

EDIT:

Here is all the code for the NHibernate Registration process

    public NHibernateRegistry()
    {
        FluentConfiguration fluentConfig = Fluently.Configure()
            .Database(
                MsSqlConfiguration.MsSql2008.ShowSql().ConnectionString(x => x.FromConnectionStringWithKey("conn")))
            .ProxyFactoryFactory(typeof (ProxyFactoryFactory).AssemblyQualifiedName)
            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerMap>());


        Configuration configuration = fluentConfig.BuildConfiguration();

        ConfigureNhibernateValidator(configuration);

        ISessionFactory sessionFactory = fluentConfig.BuildSessionFactory();

        For<Configuration>().LifecycleIs(new HybridLifecycle()).Singleton().Use(configuration);

        For<ISessionFactory>().LifecycleIs(new HybridLifecycle()).Singleton().Use(sessionFactory);

        For<ISession>().LifecycleIs(new HybridLifecycle())
            .Use(x => x.GetInstance<ISessionFactory>().OpenSession());

        For<IUnitOfWork>().LifecycleIs(new HybridLifecycle())
            .Use<UnitOfWork>();

        For<ITssPrincipal>().HybridHttpOrThreadLocalScoped().Use(container => BuildUserInstanceFromThreadCurrentPrincipal());

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

EDIT: DI problem example In the below example, you see tha RepoA is DI'd with RepoB and both get a UnitOfWork supplied by StructureMap.

public RepoA(IUnitOfWork unitOfWork, ITssPrincipal principal,
     IRepoB repoB)
{
}

public RepoB(IUnitOfWork unitOfWork, ITssPrincipal principal)
{
}

Even if I create a new session in a function in RepoA, repoB will still be using the original UnitOfWork Session

Upvotes: 1

Views: 977

Answers (1)

Randy Burden
Randy Burden

Reputation: 2661

It looks like your MVC website is using StructureMap as it's dependency injection container which makes it very easy to do what you want.

You have a couple of options but the easiest would be to just request a new instance of ISession from StructureMap. That will return you a new and different ISession than the one being used by the UnitOfWork.

Here's an example:

var session = StructureMap.ObjectFactory.GetInstance<ISession>();

using ( var tx = session.BeginTransaction() )
{
    try
    {
        // Do your work here

        tx.Commit();
    }
    catch ( Exception )
    {
        tx.Rollback();

        throw;
    }
}

Because you are using a DI container, you could also leverage StructureMap's dependency injection and have a new ISession injected into the constructor of your controller/class/repository, etc. that way you don't have to call StructureMap's ObjectFactory.GetInstance<>() method.

Upvotes: 3

Related Questions