Traditional Repositories vs single UnitOfWork

I would like to start a debate on traditional repositories versus one built on the UnitOfWork pattern.

In a traditional repository, we wrap inserts, updates and deletes in a transaction, but selects don’t really need them. In addition, nhibernate configuration is typically done once per application due to BuildSessionFactory being rather expensive. We often have several repositories of this type (one per aggregate chain). We also need to have projected, ahead of time, exactly what kinds of queries we’ll need in order to build the interfaces and concretes correctly.

In a single UnitOfWork repository we have the ability to manage both the current session and transaction, perform several operations, and then commit any changes all at once. In such a repository we may opt to put our configuration in the constructor since (typically speaking) no more than one Action is called per request anyway, initializing it in the application doesn’t seem like much of an overall gain in speed.

I must admit, I’m very temped to use the UnitOfWork pattern, as the following code looks really nice to me:


 
using (IUnitOfWork worker = new UnitOfWork())
{
// session and transaction are now both set
 
// save changes to three items
worker.SaveOrUpdate(item1);
worker.SaveOrUpdate(item2);
worker.SaveOrUpdate(item3);
 
// grab a fourth 
var item4 = worker.Criteria<Foo>().Add(Expression.Eq("title", title)).UniqueResult<Foo>();
 
// delete the fourth
worker.Delete(item4);
 
// all pending operations are commited or rolled back as a single unit (if one fails, all are rolled back), and then disposed, along with the session
}
 

My proposed UnitOfWork class is rather simple. It implements IDisposable where I use a try-catch-finally in Dispose() to attempt a tx.Commit(), doing a tx.Rollback on failure, and a cleaning everything up in the finally clause. It also exposes common things like SaveOrUpdate, Delete, GetAll, Get, and even a Critera<T>() for custom on-the-spot queries.

So what are your thoughts on this? I’d really like to hear about your experience with this pattern.

I feel transaction management is a cross-cutting concern, and use interceptors to do manage them. I don’t think the repositories responsibility includes transaction management. We run a unit of work across the request applied the same way. Calling .Resolve like that is a bad idea, its injecting infrastructural concerns where it doesn’t belong.

We don’t use a repository per aggregate root either, we don’t fully use DDD and it doesn’t add anything for the types of project do.

Then why not merge the two concepts.

Simplistically…


 
public class Repository<T> : IRepository<T> where T : Entity
{
 
protected ISession Session { get; private set; }
protected ITarnsaction Transaction { get; private set; }
 
public Repository()
{
// setup session and Transaction
}
 
}
 
public void Dispose()
{
 
try { Transaction.Commit(); }
catch { Transaction.Rollback(); }
finally
{
Transaction.Dispose;
Session.Close();
Session.Dispose();
}
 
}
 
// normal base repository methods
 

Then simply derive your actual repositories from it:


 
public CategoryRepository : Repository<Category>, ICategoryRepository
{
// add additional implementation here
}
 

And use it just like a UnitOfWork?


 
using (var cr = container.Resolve<ICategoryRepository>())
{
 
// add some categories in a batch
cr.Insert(c1);
cr.Insert(c2);
cr.Insert(c3);
}
 
// if any of the above fails, the entire batch is now rolled back instead of just the one that failed.
 

Best of both worlds, no?

Hm. Interesting points. I’d really love to hear what others are doing too. The more input, the better.

Anyway, that Resolve was just for the little demo code. I wouldn’t do that in real life either. =)

I’m curious as to how you apply it at the request level, using Application_BeginRequest and Application_EndRequest maybe? That might also be a decent solution.

Anyway, the problem I was trying to overcome was where I had an action that needed to mass move some related things, and if one failed, the result wouldn’t make sense. I needed a way of batching it.

btw…this isnt’ about my forum app (even though I used it in my demo code above). It’s something else entirely.

We use repositories because a lot of the transaction, error handling, etc. are done transparently with AOP. Also, we have a lot of junior developers, so the less lower level details they have to deal with the better, meaning they can get things done easier and quicker.