in Coding

EF Code First with the Repository and Unit of Work Patterns

I’ve started applying some patterns to my data layer today. I’m going with the standard approach of using repository pattern – this is a fairly simple pattern which hides away all the database details away from calling classes and allows them to treat database records like they’re in-memory collections. I’ve used this pattern a lot before – in my previous work the code generation software put the data access code in a repository pattern – but there are a few updates that can be made to take full advantage of the entity framework.

The entity framework gives you a DbContext object – this is the object that allows you to intereact with the database by giving you access to the types you’ve defined in your model. You make these available as properties of type DbSet. Here is an example from my project:


public class Context : DbContext
{
public DbSet Permissions { get; set; }
public DbSet Roles { get; set; }
public DbSet Users { get; set; }
}

In my first go at writing the data layer, I created a regular class which contained a bunch of methods from performing standard database activities (add, removing, saving entities to the database). Here’s an example of the kind of method I was writing:


///

/// Persists a user to the database.
///

public static AddUserResults AddUser(User user)
{
using (var db = new DbContext())
{
var existingUser = db.Users.Find(user.Username);
if (existingUser != null)
{
return AddUserResults.UserAlreadyExists;
}

db.Users.Add(user);
db.SaveChanges();
return AddUserResults.UserAdded;
}
}

Note that the method creates it own instance of the DbContext, does some work, and then calls the SaveChanges method on the DbContext. This works absolutely fine. However, what if my calling class wants to perform some other database activities as part of a single transaction? At this point each of my methods spins up it’s own copy of DbContext, creates it’s own HTTP request, and they can’t be bundled up in a transaction.

To solve this, we need to make use of another pattern, called the Unit Of Work pattern. This involves maintaining a list of objects that have been affected by a business transation. It is responsible for coordinating the saving of these changes and the resolution of any concurrency problems.

The DbContext maintains a dictionary of all items it’s read from the database and any changes made to them during a single ‘unit of work’ transaction. This information is called in a store called the “Object Cache”, and each entity is stored against it’s key value (for example the ‘username’ for my User objects). This approach is another pattern: the Identity Map pattern.

As each object is read from the database, a check is performed to see if an object with the same key already exists in the Object Cache. If it doesn’t, the object is added to the cache. If it is, then it’s retrieved from the cache itself.

For example, if I create a DbContext and then use it to load a user called “mattdurrant” it will a copy of that user object into the object cache. If I then change that users surname it will update the details in the object cache. If I then retrieve the object from the database again it will carry on using the version from the Object Cache (with the altered surname). If I then grab a list of all users from the database, it will add all of them to the Object Cache, except for the “mattdurrant” user which will again be read from the Object Cache.

Note that this is different from caching the results of a database search. In fact the database query still happens even if that object is already in the Object Cache. None of this is being done for performance – it’s done to track the states of objects.

This means that during the Unit of Work you’re working with objects in memory, and they may represent a different state from what’s in the database. You can access this collection by using the Local properties of one of the DbSet collections (e.g. Context.Users.Local in my example).
Also if you create a second DbContext it will maintain it’s own cache, and object’s may have different values from what’s in your first DbContext’s object cache. Of course this can lead to concurrency issues if you have more than one DbContext working on the same entities at once.

Once I’ve completed my unit of work I can call the SaveChanges method on the DbContext object. This will then write all of the changes that have been tracked to the database.

So how does all of this information affect the design of my data layer? Firstly I’ve created a completely generic repository that allows me to pass in any type of object:


public interface IDataRepository
{
void Add(T entity);
void Remove(T entity);
IQueryable Find(Expression> predicate);
}

Here’s the implementation:


public class DataRepository : IDataRepository
where T : class
{
Context _context;
DbSet _dbSet;

public DataRepository()
: this(new Context())
{
}

public DataRepository(Context context)
{
_context = context;
_dbSet = context.Set();
}

public void Add(T entity)
{
_dbSet.Add(entity);
}

public void Remove(T entity)
{
_dbSet.Remove(entity);
}

public IQueryable Find(Expression> predicate)
{
return _dbSet.Where(predicate);
}
}

A few things to note – although there are methods to add, remove and search there is no saving done here. If we want to implement the Unit of Work pattern, we need our calling class to tell us when to persist our changes. Also, the constructor allows a context to be passed in – this means that we can use a single context for a bunch of calls, which again supports our unit of work pattern.

Here’s my interface for the Unit of Work pattern:


public interface IUnitOfWork
{
IDataRepository UserRepository { get; }
void SaveChanges();
}

and here are some relevant parts of the implementation:


public class UnitOfWork : IUnitOfWork
{
private Context context = new Context();
private IDataRepository _userRepository;

public IDataRepository UserRepository
{
get
{
if (_userRepository == null)
{
_userRepository = new DataRepository(context);
}
return _userRepository;
}
}

public void SaveChanges()
{
context.SaveChanges();
}
}

The unit of work creates the context we’ll be using and also has access to all the available repositories (just the user repository is shown above). This means we can queue up a bunch of different actions in our various repositories and then run them all in one go.

From my calling class I can then write something that looks like this:


IUnitOfWork unitOfWork = new UnitOfWork();
var mattdurrantUser = unitOfWork.UserRepository.Find(u => u.Username == "mattdurrant").Single();
mattdurrantUser.FirstName = "Matthew";
var gemmadurrantUser = unitOfWork.UserRepository.Find(u => u.Username == "gemmadurrent").Single();
gemmadurrantUser.IsBlocked = false;
unitOfWork.SaveChanges();

Love it!