Codementor Events

Unit of work over Entity Framework

Published Apr 28, 2020

Many unit of work and repository implementations over Entity Framework do not respect the interfaces of the patterns correctly, and therefore the abstractions do not serve any purpose.

In my opinion, the single reason for (and goal of) abstracting unit of work and repository again, on top of Entity Framework’s database context and its entity sets, would be to allow changing the data access infrastructure (technology) in an application easier, i.e. without altering the application controllers (data access layer clients) themselves. If you know that you’ll never need that, use the unit of work and repository pattern implementations already provided by Entity Framework itself!

In case you really need to abstract things again, start from re-reading and understanding the patterns. Unit of work pattern should be defined in .NET using an interface like this:

public interface IUnitOfWork
{
  // Change notification methods.
  void RegisterNew(object entity);
  void RegisterDirty(object entity);
  void RegisterClean(object entity);
  void RegisterDeleted(object entity);
  // Commit and rollback methods.
  void Commit();
  void Rollback();
}

Have you ever seen such an interface implementing the unit of work pattern over Entity Framework this way? Probably you don’t find one on the Internet with ease. And this is because DbContext objects generated by Entity Framework are already good unit of work implementation themselves, as they have the powerful SaveChanges method and having automatic entity change management behavior in place offered through its IDbSet based repositories, and there is little interest to respect the true pattern. Also, some so-called repository implementations do not abstract the repository pattern over database entity sets away from database context, but are actually built on top of database context objects, which is, in my opinion, neither correct nor very useful.

From an architectural point of view I think we need to be able to change the data access technology from Entity framework to something else without modifying the application’s core. And we can avoid this unwanted dependency by respecting the pattern: in that case, we’d only need to implement the interface again using a different set of underlying services.

And what do we do when Register* methods get called? Well, who says we need to do anything?

public void RegisterNew(object entity) { }
  public void RegisterDirty(object entity) { }
  public void RegisterClean(object entity) { }
  public void RegisterDeleted(object entity) { }

But we’ll do whatever needed when we’d implement the interface over a different technology, so the methods must exist.

Repository interface is easier to provide. We can rely on .NET core types such as IEnumerable<T> and Func<T, bool> to achieve what we need:

public interface IRepository<T>
{
  // Get methods.
  IEnumerable<T> GetAll();
  IEnumerable<T> Get(Func<T, bool> query);
  T GetSingle(Func<T, bool> selector);
  // Update methods.
  void Add(T item);
  void Remove(T item);
}

As you can see, this way unit of work and repository patterns are completely disjunct. None of them references the other. Many implementations found online will enforce a link, probably due to the way DbContext objects are generated by Entity Framework, including IDbSet<T> repositories for each model entity type as part that context. But that’s just specific for that one technology, and it’s not nice to have that link at generic level we want to abstract things out to.

Another concern is to be able to provide unit of work and repository implementations on top of Entity Framework (specific for that technology) that are still generic enough that it can be reused in different applications (for different database context instances, i.e. different entity models). This is possible indeed by simply adapting our pattern interfaces to Entity Framework’s core interfaces: IObjectContextAdapter and IDbSet<T>:

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
  public EntityFrameworkUnitOfWork(IObjectContextAdapter objectContextAdapter)
  {
   ...
public class EntityFrameworkRepository<T> : IRepository<T> where T: class
{
  public EntityFrameworkRepository(IDbSet<T> entities)
  {
    ...

And now we’re ready for a client app (or, more often, client data access service). There we can either use EntityFrameworkRepository<T> class with specific entity types as type arguments or define custom repository classes if needed, inheriting from that base. Anyway we do it, the client code can then create its own DbContext as usual but then perform all data reading and writing through the unit of work and repository pattern implementations!

using (var sampleDbContext = new SampleDatabaseEntities())
{
  IUnitOfWork unitOfWork = new EntityFrameworkUnitOfWork(sampleDbContext);
  IRepository<Department> departmentRepository =
    new EntityFrameworkRepository<Department>(sampleDbContext.Departments);
  ...
  var developmentDepartment =
    departmentRepository.GetSingle(d => d.Name == "Development");
  ...
  var newDeveloper =
    new Employee {
      FirstName = "John", LastName = "Daniels",
      Department = developmentDepartment };
  employeeRepository.Add(newDeveloper);
  unitOfWork.RegisterNew(newDeveloper);
  ...
  unitOfWork.Commit();
  ...

Of course, we can further abstract things and define an IDataAccessService interface and a generic EntityFrameworkDataAccessService<TDbContext> implementation to represent a factory for unit of work and repository instances for a specific database context type, and then the client side will simply use that one to get the references it needs for IUnitOfWork and IRepository<T>, but although that’s a great thing to do, it’s out of the scope of this discussion.

Maybe a diagram expresses the intended output even better:

Data access diagram

Feel free to use this approach in your applications whenever needed. I have managed to put the entire source code, including a small sample app, into a GitHub repository. Enjoy!


Originally posted on WordPress on March 15, 2016.

Discover and read more posts from Sorin Dolha
get started
post commentsBe the first to share your opinion
Show more replies