Reputation: 621
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
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