Encapsulating Entity access in EntityFramework by using the Repository pattern
January 11, 2010 14 Comments
EntityFramework provides a very easy way to query your entities and thus the database but it creates a very strong dependency between the calling code and the data access layer(EF in this case).
The Repository pattern allows you to encapsulate any entity access logic and also to decouple your business logic layer from your data access layer, in other words, enables separation of concerns, plus it makes unit testing a lot easier.
Consider the following entity data model:
And a WCF Service that exposes Orders and its aggregated OrderDetails:
public class OrderService : IOrderService { private MFEntitiesContainer _context = new MFEntitiesContainer(); public IEnumerable<Order> ListOrders() { return _context.OrderSet.Include("OrderDetails") .Where(o => o.Customer.Active); } public IEnumerable<Order> ListOrdersByCustomer(int customerId) { return _context.OrderSet.Include("OrderDetails") .Where(o => o.Customer.Active && o.Customer.Id == customerId); } }
Lets see how we can refactor the OrderService to use the Repository pattern instead of querying the EF’s ObjectContext.
Consider that when working with Order entities its OrderDetails must always be loaded and that I can only work with Orders whose Customer is still active.
Notice that I had to code the customer is still active predicate and that if I’m querying Order entities in any other place I will have to write it there too. I’m also using EntityFramework lazy-load access logic (.Include call) everywhere which means that if someday I want to switch my DAL to NHibernate I might have to refactor a lot of code.
The Repository pattern is very useful in these cases and is often implemented with a generic interface that looks like this:
public interface IRepository<TEntity> { IQueryable<TEntity> Query(); void Add(TEntity entity); void Attach(TEntity entity); void Delete(TEntity entity); void SaveChanges(); }
Thanks to EntityFramework’s ObjectContext class we can implement a base repository class that works for any entity:
public class EntityRepository<TEntity> : IRepository<TEntity> where TEntity : EntityObject, new() { public EntityRepository(ObjectContext context) { this.ObjectContext = context; this.FetchEntitySetName(); } protected ObjectContext ObjectContext { get; private set; } protected string EntitySetName { get; private set; } //looks for an IQueryable<TEntity> property in the ObjectContext //and gets its name to be used in other methods private void FetchEntitySetName() { var entitySetProperty = this.ObjectContext.GetType().GetProperties() .Single(p => p.PropertyType.IsGenericType && typeof(IQueryable<>) .MakeGenericType(typeof(TEntity)).IsAssignableFrom(p.PropertyType)); this.EntitySetName = entitySetProperty.Name; } //to be implemented by derived classes if needed protected virtual IQueryable<TEntity> BuildQuery(ObjectQuery<TEntity> query) { return query; } public virtual IQueryable<TEntity> Query() { var entitySet = String.Format("[{0}]", this.EntitySetName); var baseQuery = this.ObjectContext.CreateQuery<TEntity>(entitySet); return this.BuildQuery(baseQuery); } public virtual void Add(TEntity entity) { this.ObjectContext.AddObject(this.EntitySetName, entity); } public virtual void Attach(TEntity entity) { this.ObjectContext.AttachTo(this.EntitySetName, entity); } public virtual void Delete(TEntity entity) { if (entity.EntityState == EntityState.Detached) this.ObjectContext.AttachTo(this.EntitySetName, entity); this.ObjectContext.DeleteObject(entity); } public void SaveChanges() { this.ObjectContext.SaveChanges(); } }
Notice the BuildQuery method that we can use in derived classes if we need to provide base queries to access a specific entity. In this case we would create an OrderRepository class like the following:
public class OrderRepository : EntityRepository<Order> { public OrderRepository(MFEntitiesContainer context) : base(context) { } protected override IQueryable<Order> BuildQuery(System.Data.Objects.ObjectQuery<Order> query) { return query.Include("OrderDetails").Where(o => o.Customer.Active); } }
If you’re familiar with Inversion of Control and Dependency Injection concepts you’re already thinking on how to refactor the OrderService in order to use the OrderRepository implementation but depend only on the IRepository interface. For this post I didn’t consider using any specific IoC container so I hard-coded one that works, although I’m a big fan of Unity
public static class YourFavIoCContainer { //only works for IRepository<T> for blog purposes.. public static T Resolve<T>(params object[] constructorParameters) { Type repositoryType = null; if (typeof(IRepository<Order>).IsAssignableFrom(typeof(T))) repositoryType = typeof(OrderRepository); else if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(IRepository<>)) { Type entityType = typeof(T).GetGenericArguments()[0]; repositoryType = typeof(EntityRepository<>).MakeGenericType(entityType); } else throw new InvalidOperationException("Only IRepository<T> implementations can be resolved"); return (T)Activator.CreateInstance(repositoryType, constructorParameters); } }
The OrderService can now be refactored to use the Repository pattern like this:
public class OrderService : IOrderService { private MFEntitiesContainer _context = new MFEntitiesContainer(); public IEnumerable<Order> ListOrders() { return YourFavIoCContainer.Resolve<IRepository<Order>>(_context).Query(); } public IEnumerable<Order> ListOrdersByCustomer(int customerId) { return YourFavIoCContainer.Resolve<IRepository<Order>>(_context) .Query().Where(o=>o.Customer.Id == customerId); } }
Some folks don’t like to do custom queries outside the Repository world so for cases like the ListOrdersByCustomer operation you can also encapsulate the customer predicate in the OrderRepository by creating an IOrderRepository interface that derives from IRepository<Order>:
public interface IOrderRepository : IRepository<Order> { IQueryable<Order> QueryByCustomer(int customerId); }
public class OrderRepository : EntityRepository<Order>, IOrderRepository { public OrderRepository(MFEntitiesContainer context) : base(context) { } protected override IQueryable<Order> BuildQuery(System.Data.Objects.ObjectQuery<Order> query) { return query.Include("OrderDetails").Where(o => o.Customer.Active); } public IQueryable<Order> QueryByCustomer(int customerId) { return this.Query().Where(o => o.Customer.Id == customerId); } }
And the refactored ListOrdersByCustomer operation should look like this:
public IEnumerable<Order> ListOrdersByCustomer(int customerId) { return YourFavIoCContainer.Resolve<IOrderRepository>(_context).QueryByCustomer(customerId); }
Now we only need to get rid of the MFEntitiesContainer (the ObjectContext instance) since it represents our last dependency to Entity Framework objects. I shouldn’t be creating nor managing the ObjectContext instance in my service but instead It should be managed using a unit of work pattern that should last as long as my service is activated (works well with WCF’s SessionMode).
Since the purpose of this post was all about the Repository pattern I’ll try to address the issues I mentioned above in another post (soon I hope!). I’ve done something similar before with NHibernate using Sharp-Architecture and WCF, so I might try that here. If anyone has thoughts on the matter please contribute!
Happy coding!

I just posted follow-up post for this blog entry to solve the problem described at the end of this post.
Pingback: Managing the Entity Framework ObjectContext instance lifetime in WCF and sharing it among repositories « Manuel Felício's Weblog
public class EntityRepository : IRepository
where TEntity : EntityObject, new()
{
public EntityRepository(ObjectContext context)
{
this.ObjectContext = context;
this.FetchEntitySetName();
}
When I try to access this.ObjectContext… No such thing exists what am I missing?
Nevermind I am a moron…
Pingback: Architecting Enterprise LOB Silverlight applications – Part 1 « Manuel Felício's Weblog
I’m trying to get my head around EF and your post has helped me immensely. Our DB design is such that we don’t do hard deletes, so all reads have to include the clause ‘IsDeleted ==0′. Your method will make our code much DRYer. Thank you!
Hello Dean,
I’m glad to hear that my post helped you.
Indeed, the repository is the ideal place to encapsulate the IsDeleted clause.
Thanks for your feedback,
Manuel Felício.
Hi,
I’d like to know to which version of EF this does apply?
V1 or V4 ?
Thx,
Ced
Hello Ced,
It applies to to both versions.
Best regards,
MF.
So each and every time I would like to add a single method to the repository (add more functionality other
then the base repository) I would have to add a new interface and a new line in the initialization of my IoC?
It looks like a lot of work for one method..
Are there any other possibilities to encapsulate the entity access?
@Naor,
Of course not.. that would be a lot of work indeed. You just need to create a new interface and put all the new methods there.
For example, if you want to add functionality that only applies to Orders, you would create an IOrderRepository and add methods there. If you want to add functionality that applies to all entities you can create a new interface (and put the methods there) that derives from IRepository, create a new base class for your repositories that extends the base Repository class and implements that new interface.
Your IoC must be updated as well, if you make your classes depend on the new interfaces.
So you agree with me that is a lot of work..
What I did:
I have IRepository and its EFRepository implementation.
I have created a new class BaseRepository that implemented IRepository and using the IoC initialize the EFRepository instanse (or any other implementation).
Now, in oder to add newfunctionality I only need to inherit BaseRepository.
You can see detailed answer here: http://stackoverflow.com/questions/5784852/extension-to-default-repository-class/5795407#5795407
Yes, it is a lot of work but that isn’t the way i encourage you to add new functionallity anyway.
Creating a new base class for repositories is the way!
Pingback: Confluence: Abiliton SaaS