views:

154

answers:

1

Although I'm not doing full blown DDD, I find the repository pattern appealing and I do try to segment the repositories along aggregate-root boundaries. I'm implementing repositories on top of the Entity Framework, and here the ObjectContext allows for a unit-of-work style as it tracks changes to the entities and will generate the appropriate SQL when SaveChanges is called.

I'm struggling with two different approaches within my repositories around when to call SaveChanges -- and the difference seems to be whether I'm adopting unit-of-work or active record semantics. If I define a repository interface to look like this:

public interface IRepository<T>
{
    T Get(int id);
    IList<T> GetAll();
    IQueryable<T> Query();
    void Delete(T entity);
    void Add(T entity);
    IUnitOfWork GetUnitOfWork();
}

and IUnitOfWork to be

public interface IUnitOfWork
{
    void SaveChanges();
}

Then in the Add(T entity) implementation, I seem to have two choices:

    public void Add(Document entity)
    {
        DB.AddToDocumentSet(entity);
        GetUnitOfWork().SaveChanges();  //delegates to the ObjectContext's SaveChanges
    }

or

    public void Add(Document entity)
    {
        DB.AddToDocumentSet(entity);
    }

In the former case, the repository's Add method is sending the SQL on each operation. In the latter case, the calling code is responsible for obtaining the unit of work from the repository and calling SaveChanges when it deems appropriate. This allows the unit of work to span different repositories (I'm ensuring that each repository gets the same unit of work in its construction).

My gut feel is that the second approach is more flexible. Adopting the unit of work pattern also means that updates to entities are a bit nicer in that calling code simply updates properties on entities returned by the repositories and then calls UnitOfWork.SaveChanges.

When using the repository pattern, is one approach generally favored over the other?

+2  A: 

It depends on your failure mode requirements, which you haven't stated. This is primarily an issue of how to handle failures that occur during your unit of work. You have basically two choices, although there are other more complex variants possible:

  1. Save all changes that occurred during the unit of work up until the failure.
  2. Rollback all changes that occurred during the unit of work due to the failure.

Your first Add method is more appropriate for scenario 1 and your second Add method is more appropriate for scenario 2.

Scenario 1 likely requires smart application code to allow the user to resume at the point of failure. Scenario 2 is easier to implement on the application side, but may frustrate the user if they were, for instance, 8 steps into a 9 step process when it failed.

Michael Maddox