Entity Framework POCO (EF4): Generic Repository and Unit of Work Prototype

December 15th, 2009

In my previous post I demonstrated how to create a simple mapping using the latest EF4 CTP. In this post I will look at how I can customize some infrastructure code in an attempt to align EF POCO “Code Only” with existing patterns, while potentially increasing reuse and testability.

A popular pattern for ORM data access is the Repository pattern. Implementing a generic repository in EF4 gets much easier with ObjectSet<T>. One might argue that it is, in a sense, a Repository.

What I would like to do is “Adapt” this interface to my own generic Repository. This will let me customize the interface to my own specifications and remove the dependency in my domain services on ObjectSet<T>.

Here is a great article based on the last CTP that does a nice job of laying out a Repository<T> strategy with a UnitOfWork, and here is another using nHibernate (see also UnitOfWork examples). If you are not familiar with the Generic Repository pattern and UnitOfWork, you should take a quick second and skim these before you continue. I want to take this implementation and try and change just a few things about it.

  • Static Unit of Work reference: First, I want to remove the Repository static reference to the UnitOfWork, which is how the Repository in the examples are resolving the ObjectContext/Session. Aside from not being a fan of static classes, I think it is the application Container’s responsibility to manage dependency resolution. Plus, I don’t really know that I want my Repository to have any reference to the UnitOfWork. I want the UnitOfWork to span across multiple repositories within a given transaction.
  • Object Lifetime: I would like to have more granular control over the UnitOfWork and ObjectContext lifetime. In the authors example, the UnitOfWork is managing the lifetime strategy, which is something the Container is perfect at handling. I will leave the UOW to transaction management only.
  • Constructor Injection: I think since the Repository has a hard dependency on the ObjectContext, that it would make the most sense to have the ObjectContext injected into Repository ctor.
  • Concrete ObjectContext reference: Since I will now be passing the ObjectContext into the ctor, I feel better if this was an interface. Not that I foresee a tremendous amount of testable value here, but I still don’t like taking a ctor dependency on a framework object if I can avoid it. Plus, this is prototype code, so why not?

Repository Interface

You will notice the interface remains unchanged from the first referenced article (Update: changed Func<T, bool> to Expression<Func<T, bool>> so that expressions can be evaluated correctly by ObjectQuery)

public interface IRepository<T> where T : class
{
    IQueryable<T> AsQueryable();
 
    IEnumerable<T> GetAll();
    IEnumerable<T> Find(Expression<Func<T, bool>> where);
    T Single(Expression<Func<T, bool>> where);
    T First(Expression<Func<T, bool>> where);
 
    void Delete(T entity);
    void Add(T entity);
    void Attach(T entity);

}

 

Generic Repository Implementation

As I described in the bullets above, my implementation does in fact take the IObjectContext as a ctor argument. Also, notice that I do not have any static references.

public class Repository<T> : IRepository<T> where T : class
{
   IObjectSet<T> _objectSet;
 
   public Repository(IObjectContext objectContext)
   {
       _objectSet = objectContext.CreateObjectSet<T>();
   }
 
   public IQueryable<T> AsQueryable()
   {
       return _objectSet;
   }
 
   public IEnumerable<T> GetAll()
   {
       return _objectSet.ToList();
   }
 
   public IEnumerable<T> Find(Expression<Func<T, bool>> where)
   {
       return _objectSet.Where(where);
   }
 
   public T Single(Expression<Func<T, bool>> where)
   {
       return _objectSet.Single(where);
   }
 
   public T First(Expression<Func<T, bool>> where)
   {
       return _objectSet.First(where);
   }
 
   public void Delete(T entity)
   {
       _objectSet.DeleteObject(entity);
   }
 
   public void Add(T entity)
   {
       _objectSet.AddObject(entity);
   }
 
   public void Attach(T entity)
   {
       _objectSet.Attach(entity);
   }

}

 

Unit of Work

Just like EF4 ObjectSet does a lot out of box for our Repository, the EF ObjectContext really does a lot of the work for us in regards to managing the UnitOfWork. The Object context is already capable of handling a transaction across many operations over different types. So, again, we are going to just add a little wrapper around the context and call it a UnitOfWork. This will give me a nice hook where my infrastructure can snag a reference to the ObjectContext and Commit the transaction changes, for example, at the end of a WCF, Web or DataService request.

interface IUnitOfWork
{
    void Commit();
}
 
public class UnitOfWork: IUnitOfWork, IDisposable
{
    private readonly IObjectContext _objectContext;
 
    public UnitOfWork(IObjectContext objectContext)
    {
        _objectContext = objectContext;
    }
 
    public void Dispose()
    {
        if (_objectContext != null)
        {
            _objectContext.Dispose();
        }
        GC.SuppressFinalize(this);
    }
 
    public void Commit()
    {
        _objectContext.SaveChanges();
    }
}

 

The Object Context Adapter

Just some Adapter 101 code here to abstract away the Concrete Context

public interface IObjectContext : IDisposable
{
    IObjectSet<T> CreateObjectSet<T>() where T : class;
    void SaveChanges();
}
 
public class ObjectContextAdapter : IObjectContext
{
    readonly ObjectContext _context;
 
    public ObjectContextAdapter(ObjectContext context)
    {
        _context = context;
    }
 
    public void Dispose()
    {
        _context.Dispose();
    }
 
    public IObjectSet<T> CreateObjectSet<T>() where T : class
    {
        return _context.CreateObjectSet<T>();
    }
 
    public void SaveChanges()
    {
        _context.SaveChanges();
    }
}

 

Using the Repository

Without out my application Container, here is how I would code up the new abstractions:

//...
// a bunch of ugly set up code here, see previous post
//...
 
var context = builder.Create(connection);
var contextAdapter = new ObjectContextAdapter(context);
var unitOfWork = new UnitOfWork(contextAdapter);
 
var teamRepository = new Repository<Team>(contextAdapter);
var newTeam = new Team { Name = "Da Bears" };
 
teamRepository.Add(newTeam);
unitOfWork.Commit();
 
var team = teamRepository.First(x => x.Name == "Da Bears");
Console.WriteLine(team.Name);
 
context.Dispose();

Admittedly, this is not very pretty and is still way more than I want to deal with on regular basis. In my next post I will demonstrate how I will tie all this together, EF Configuration, Repository and UnitOfWork using a Unity Extension.

Please keep in mind this is prototype, untested code on a CTP release. Code at your own risk!

Jarod Ferguson Entity Framework

  1. Nikola
    January 20th, 2010 at 00:08 | #1

    English is not my native language, so I apologize for mistakes.
    Take a look at these two queries:

    var emps = from emp in repository.GetQuery()
    where emp.EmployeeID > 3
    select emp;

    var emps = repository.Find(e => e.EmployeeID > 3);

    when you start SQL Server profiler trace session, you can see that first one translates to something like this:

    “SELECT (allcolumns) FROM Employee WHERE EmployeeID > 3″

    while the second one translates to something like this

    “SELECT (allcolumns) FROM Employee” (without WHERE clause)

    Shouldn’t both queries translate to the same SQL query (the one with WHERE clause) ??

  2. January 20th, 2010 at 18:49 | #2

    @Nikola – Good catch. Single() & First() were broken too. I have updated the Repository impl to use an Expression as parameters instead of Func. ObjectQuery can now evaluate the expressions and generate the correct SQL.

    ———-
    * Since I was modifying the sample, I also removed SaveChanges() from the Repository as per Damiens suggestion (thus also removing the ObjectContext member var). Transaction should be managed only thru UOW now.

  3. February 20th, 2010 at 03:12 | #3

    very clean. I like it!

  4. February 21st, 2010 at 01:10 | #4

    cool…i like it

Comment pages
Comments are closed.