Wednesday, June 1, 2011

Basic Unit Of Work Implementation

    I bet you must have heard about Unit Of Work. If you have not, you can find the discription here : http://martinfowler.com/eaaCatalog/unitOfWork.html.    You can change the database with each change to your object model, but this can lead to lots of very small database calls, which ends up being very slow. Furthermore it requires you to have a transaction open for the whole interaction, which is impractical if you have a business transaction that spans multiple requests. The situation is even worse if you need to keep track of the objects you've read so you can avoid inconsistent reads.     Nowadays, DDD is one of the most famous design methodologies in the developer community. Even though 95% of all software applications fall into the “not so good for using DDD” categories, some practices and ideas of DDD can be used in many .NET projects, especially Repository and Domain Service. I have seen many implementations of Repository pattern and UnitOfWork, but i am not so happy with any because of the following reason. We tend to use some kinds of ORM like NHibernate or EF as the technology behind Repository implementation. These frameworks mostly have the Unit Of Work built in, so I think the Domain Service layer should not call IUnitOfWork.CommitChange() when it needs to persist something to the repository. In some complex services, we call a service method which may execute different service methods; and because a service method (that requires changing the database) should call IUnitOfWork.CommitChanges. Therefore, as a result, IUnitOfWork.CommitChanges will be called many times in 1 business transaction.     Another example could be the controller which can use several domain services to execute an action method. Let's say a website user wants to register at your website. When he submits the registration form, the RegisterController will call the UserService to create a user to the repository and another service create a WelcomeEmail in the Email repository. That means there are 2 records are got involed in this transaction (assume that you use SQL as the data source). And a proper UnitOfWork implementation should be called only 1 time to finish the saving task. However, it's easy to see the implementation like this:

[HttpPost]
public ActionResult Register(RegisterUserViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        return View(viewModel)
    }
    
    UserService.Create(user);
    EmailService.Create(welcomeEmail);
    
    return RedirectToAction("Index", "Home");
}
In the user service implementation :
public void Create(User contact)
{    
    _userRepository.Add(contact);
    _unitOfWork.CommitChange();
}
And in the email service implementation :
public void Create(Email email)
{    
    _emailRepository.Add(email);
    _unitOfWork.CommitChange();
}
    It's not difficult to see that the UnitOfWork will call method CommitChange() two times. If you use EF behind the Repository implementation, I'm sure the code above will make your breakpoint stop at method ObjectContext.SaveChanges() 2 times. For me it’s not “Unit Of Work” if we do like that. It would be nice if we could have a way to call the CommitChange() only when we actually need, it could be the end of every http request. Therefore, the domain services should never call CommitChange() on your UnitOfWork.     Base on this idea which is a bit similar to "Open Session In View" pattern, I created a basic implementation of UnitOfWork below:
public interface IUnitOfWork
{
    void CommitChange();
    bool IsDirty { get; set; }
}
The domain service will use a UnitOfWorkScope to ensure the change will be committed in the end or immediately based on the UnitOfWorkScopeOption:
public enum UnitOfWorkScopeOption
{
    Shared,
    CommitImmediately
}

public class UnitOfWorkScope : Disposable
{
    private readonly UnitOfWorkScopeOption _option;
    private readonly IUnitOfWork _unitOfWork;

    public UnitOfWorkScope() : this(UnitOfWorkScopeOption.Shared)
    {
    }

    public UnitOfWorkScope(UnitOfWorkScopeOption option)
    {
        _option = option;
        _unitOfWork = ObjectFactory.GetInstance<IUnitOfWork>(); // I'm using StructureMap
    }

    protected override void DisposeCore()
    {
        switch(_option)
        {
            case UnitOfWorkScopeOption.CommitImmediately:
                {
                    _unitOfWork.CommitChange();
                    _unitOfWork.IsDirty = false;
                    break;
                }
            default:
                _unitOfWork.IsDirty = true;
                break;
        }
    }
}
I will change the service methods like below:
public void Create(User contact)
{    
    using (new UnitOfWorkScope())
    {
        _userRepository.Add(contact);    
    }
}

public void Create(Email email)
{    
    using (new UnitOfWorkScope())
    {
        _emailRepository.Add(email);
    }
}
The default constructor of UnitOfWorkScope would set the scope option to Shared. That means it would do nothing but mark the UnitOfWork.IsDirty to true. In the end, we can base on that value to determine whether we should call commit change. I put this logic in the base controller, so after the ActionExecuted, the UnitOfWork will be invoked to do the saving job.
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
    base.OnActionExecuted(filterContext);

    if (filterContext.Exception == null || filterContext.ExceptionHandled)
    {
        var unitOfWork = ObjectFactory.GetInstance<IUnitOfWork>();
        if (unitOfWork != null && unitOfWork.IsDirty)
        {
            unitOfWork.CommitChange();
        }
    }    
}
I made a test project and looks like it run at least 2 times faster than the the original way when I use UnitOfWorkScope. There is a very cool implementation of UnitOfWork at NCommon. You should check it out. Finally, here is the test project. Cheers

6 comments:

Mr Baldman said...

Thanks for posting this - i have the exact same issue (and ironically enough, its with a user registration service!) and this has pointed me in the right direction. :)

Mr Baldman said...

In UnitOfWorkScope.cs the build flags are the wrong way round - set it to sharedscope configuration and it uses UnitOfWorkScopeOption.CommitImmediately. easily fixed though.

Vo Thanh Toan said...

Hi Thoai, I love the way you approach the Unit Of work pattern. I would like to contribute your idea is: Instead of you repeat using (new UnitOfWorkScope()) { //TODO } you can using Func or Action or AOP to reduce repeat input. We can consider DRY here. Soon to see your idea.

shalini said...

I like your writing style. I'll definitely come back later. I'm about to bookmark your site now.
domain service

Web Hosting India said...

I was extremely pleased to find this website.I wanted to thank you for this good understand I definitely enjoying every single small bit of it.

indian web hosting companies

jautrsezis said...

The best example in UOW pattern. Easy to understand also a great aproach! I will also definitely come back later. Thank you.

Post a Comment