Let me start with my current setup, and then explain what I am trying to achieve. We are using NHibernate and trying to implement the IRepository/IUnitOfWork pattern with Ninject. It should ideally work generically for whatever application is using the code, whether is ASP.Net, WCF or something else.
IUnitOfWork
public interface IUnitOfWork
{
object Add(object obj);//all other supported CRUD operations we want to expose
void Commit();
void Rollback();
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ISession _session;
private readonly ITransaction _transaction;
public UnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
_session = _sessionFactory.OpenSession();
_transaction = _session.BeginTransaction();
}
public object Add(object obj)
{
return _session.Save(obj);
}
public void Commit()
{
if(!_transaction.IsActive)
{throw new Exception("some error");}
_transaction.Commit();
}
public void Rollback()
{
if (!_transaction.IsActive)
{
throw new Exception("some other error");
}
_transaction.Rollback();
}
}
IRepository
public interface IRepository<TEntity, TId> where TEntity : class
{
TId Add(TEntity item);//add other missing CRUD operations
}
GenericRepository
public class GenericRepository<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : class
{
public TId Add(TEntity item)
{
throw new NotImplementedException();
}
}
I am using Ninject as my IOC container. The goal is to reuse the same IUnitOfWork for the life cycle of the creating of the UnitOfWork. I want the life cycle that is implemented to work no matter what the calling application is, or else I would have just used InRequestScope like most suggestions online. I was able to do something like this:
//constructor
public MyService(IUnitOfWork uow, IRepository<User, int> userRepo, IRepository<Cat, int> catRepo)
{
_uow = uow; _userRepo = userRepo; _catRepo = catRepo;
}
//method in same class
public void DoSomeWork()
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
_uow.Commit();
//rollback on error
}
And my bindings are set up like:
Bind<IUnitOfWork>.To<UnitOfWork>().InCallScope();
Bind(typeof(IRepository<,>)).To(typeof(GenericRepository<,>));
And this binding configuration actually works for the above MyService
, it will create the UnitOfWork once in the constructor and it will use that same UnitOfWork for the IRepo impls as well, no matter how many layers down they may actually be.
But what I would like to be able to do is hide the IUnitOfWork away from the applications altogether. I would rather provide some TransactionAttribute that can be placed on top of a method and it will create the IUnitOfWork on entry and that same instance will be injected to all any future requests for an IUnitOfWork within the scope of the TransactionAttribute. And it would take care of committing and rollingback accordingly. So the previous code would become something like this:
//constructor
public MyService(IRepository<User, int> userRepo, IRepository<Cat, int> catRepo)
{
_uow = uow; _userRepo = userRepo; _catRepo = catRepo;
}
//method in same class
[Transaction]
public void DoSomeWork()
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
}
Is there any kind of binding setup I can do that will enable me to mark a method with a [Transaction] like this? I am open to some minor restructuring of the IUnitOfWork and IRepository stuff, and the Service layer code is just scrap code so I can be very flexible there.