Previously, I talked about the Repository pattern and how we could implement it using Linq (and Linq to Sql in particular). This week, I'd like to take the Linq Repository abstraction that I came up with last time, and show how we can use it to easily and transparently enable the Unit of Work pattern, using ASP.NET MVC as our host.
What's in a Unit
From Fowler, a Unit of Work "maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems" (PEAA, p. 184). The key here is that "unit" means "business transaction".
The Linq to Sql DataContext is designed perfectly to support this pattern. In fact, the MSDN documentation for it plainly states that "a DataContext instance is designed to last for one "unit of work" however your application defines that term. A DataContext is lightweight and is not expensive to create. A typical LINQ to SQL application creates DataContext instances at method scope or as a member of short-lived classes that represent a logical set of related database operations." .NET consultant and author (MSDN Magazine) Dino Esposito emphasises this point in an in-depth article over at DotNetSlackers, and also in a follow-up post on his blog.
One Request = One Action = One Unit of Work
The beauty of an MVC application, is that each request is self-contained. There's no state to worry about; each request has all the information it needs to carry out the requested action. This fits neatly with what we've just discussed - we can then say that each request, or action, is in fact a single unit of work.
I've talked about how to set up the IOC container Autofac for ASP.NET MVC before, so I won't reiterate that here. The cool thing about Autofac, is that it allows for nested containers. In fact, its ASP.NET integration module will out of the box set up a root container that is application scoped, and then a new nested container for each new request. All we have to do then, is to register our IDataContext as a container scoped object. Autofac will then ensure that throughout the lifetime of any request, any object that asks for it gets the same IDataContext instance injected:
builder.Register(c => new DataContext(ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString)).FactoryScoped();
builder.Register<LinqToSqlContext>().As<IDataContext>().ContainerScoped();
Notice that I've registered the concrete DataContext class as well - this is so that Autofac will know how to resolve the constructor for my LinqToSqlContext, which takes a DataContext as its single parameter.
Any controller class that has actions which require access to the unit of work can then be implemented like this:
public class EditController : Controller
{
private readonly IDataContext _unitOfWork;
public EditController(IDataContext unitOfWork)
{
_unitOfWork = unitOfWork;
}
public ActionResult Rename(string route, string newTitle)
{
IPage page = _unitOfWork.Repository<IPage>().Where(p => p.Route.Path == route).Single();
page.Title = newTitle;
return new RenderJsonResult {Result = new {status = "ok"}};
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
_unitOfWork.Commit();
}
}
Note that I would probably move that OnActionExecuted method to a base controller type to avoid duplicating it in all my controllers. And also note that because of the deterministic disposal functionality of Autofac, we don't have to worry about disposing the IDataContext; it (and the Linq to Sql DataContext it wraps) will be disposed with the nested container at the end of the request.
I think the power of design patterns really shows when you see how well different technologies that adhere to them are then enabled to work together in perfect harmony, like here.