Entity Framework and the Repository Pattern
Thursday, September 9th, 2010When I’ve asked how to unit test Entity Framework, the best answer was “use the Repository pattern to encapsulate your EF code” (thanks Andrew Peters). I recently asked the same thing about RIA Services. Mike Brown responded with the same advice, even going so far as to spend a couple of hours with me on Live Meeting.
Since all you gotta do is implement the Repository Pattern, it should be easy, right? Let’s take a look at a minimalist implementation.
Inject the implementation
To support unit testing, we need to be able to swap out our implementation. At the top level, the consumer of a repository begins a unit of work. So we’ll inject a unit of work factory.
public interface IUnitOfWorkFactory { IUnitOfWork Begin(); }
public class NoteworthyService { private IUnitOfWorkFactory _unitOfWorkFactory; public NoteworthyService(IUnitOfWorkFactory unitOfWorkFactory) { _unitOfWorkFactory = unitOfWorkFactory; } }
Identify the repository
In DDD we create one repository per aggregate root, which is the entity from which you begin the query. In EF, entities exist in a container. So we’ll take two steps to get the repository via the container.
public interface IUnitOfWork : IDisposable { IContainer<TContainer> UsingContainer<TContainer>() where TContainer : ObjectContext, new(); } public interface IContainer<TContainer> : IDisposable { IRepository<TEntity> GetRepository<TEntity>(Func<TContainer, ObjectQuery<TEntity>> repositorySelector) where TEntity : EntityObject; }
Provide a specification
The second half of the Repository Pattern is the Specification. A specification identifies which entities to pull from the repository. In Linq, we use lambdas for that. So a repository should be able to give you back some entities when given a lambda.
public interface IRepository<TEntity> { IQueryable<TEntity> GetSatisfying(Expression<Func<TEntity, bool>> specification); void Add(TEntity entity); }
Query the repository
With those interfaces in place, here’s what a query looks like.
public List<Article> GetArticlesByTopic(string topicName) { using (var unitOfWork = _unitOfWorkFactory.Begin()) { return unitOfWork.UsingContainer<NoteworthyEntities>().GetRepository(container => container.Articles) .GetSatisfying(article => article.Topics.Any(topic => topic.TopicName == topicName)) .ToList(); } }
Implement in memory
For unit testing, we implement the repository interfaces using in-memory lists.
public class MemoryUnitOfWorkFactory : IUnitOfWorkFactory { private MemoryUnitOfWork _unitOfWork = new MemoryUnitOfWork(); public IUnitOfWork Begin() { return _unitOfWork; } } public class MemoryUnitOfWork : IUnitOfWork { private Dictionary<Type, object> _containerByType = new Dictionary<Type, object>(); public IContainer<TContainer> UsingContainer<TContainer>() where TContainer : ObjectContext, new() { object container; if (!_containerByType.TryGetValue(typeof(TContainer), out container)) { container = new MemoryContainer<TContainer>(); _containerByType.Add(typeof(TContainer), container); } return (IContainer<TContainer>)container; } public void Dispose() { } } public class MemoryContainer<TContainer> : IContainer<TContainer> where TContainer : ObjectContext, new() { private Dictionary<Type, object> _containerByType = new Dictionary<Type, object>(); public IRepository<TEntity> GetRepository<TEntity>(Func<TContainer, ObjectQuery<TEntity>> repositorySelector) where TEntity : EntityObject { object container; if (!_containerByType.TryGetValue(typeof(TEntity), out container)) { container = new MemoryRepository<TEntity>(); _containerByType.Add(typeof(TEntity), container); } return (IRepository<TEntity>)container; } public void Dispose() { } } public class MemoryRepository<TEntity> : IRepository<TEntity> where TEntity : EntityObject { private List<TEntity> _entities = new List<TEntity>(); public IQueryable<TEntity> GetSatisfying(Expression<Func<TEntity, bool>> specification) { return _entities.Where(specification.Compile()).AsQueryable(); } public void Add(TEntity entity) { _entities.Add(entity); } }
And here’s what a unit test looks like.
[TestClass] public class NoteworthyServiceTest { private NoteworthyService _noteworthyService; [TestInitialize] public void Initialize() { IUnitOfWorkFactory memory = new MemoryUnitOfWorkFactory(); IRepository<Article> articlesRepository = memory.Begin() .UsingContainer<NoteworthyEntities>() .GetRepository(container => container.Articles); Topic ddd = new Topic() { TopicName = "ddd" }; Topic corresopndence = new Topic() { TopicName = "correspondence" }; Article efRepository = new Article() { Title = "Entity Framework and the Repository Pattern" }; efRepository.Topics.Add(ddd); Article correspondenceLaunch = new Article() { Title = "Correspondence Launch" }; correspondenceLaunch.Topics.Add(corresopndence); Article correspondenceDDD = new Article() { Title = "Correspondence and DDD" }; correspondenceDDD.Topics.Add(ddd); correspondenceDDD.Topics.Add(corresopndence); articlesRepository.Add(efRepository); articlesRepository.Add(correspondenceLaunch); articlesRepository.Add(correspondenceDDD); _noteworthyService = new NoteworthyService(memory); } [TestMethod] public void QueryReturnsArticles() { var articles = _noteworthyService.GetArticlesByTopic("ddd").ToArray(); Assert.AreEqual("Entity Framework and the Repository Pattern", articles[0].Title); Assert.AreEqual("Correspondence and DDD", articles[1].Title); } }
Implement with Entity Framework
Finally, we implement the real thing using Entity Framework.
public class EntityFrameworkUnitOfWorkFactory : IUnitOfWorkFactory { public IUnitOfWork Begin() { return new EntityFrameworkUnitOfWork(); } } public class EntityFrameworkUnitOfWork : IUnitOfWork { private Dictionary<Type, IDisposable> _containerByType = new Dictionary<Type, IDisposable>(); public IContainer<TContainer> UsingContainer<TContainer>() where TContainer : ObjectContext, new() { IDisposable container; if (!_containerByType.TryGetValue(typeof(TContainer), out container)) { container = new EntityFrameworkContainer<TContainer>(); _containerByType.Add(typeof(TContainer), container); } return (IContainer<TContainer>)container; } public void Dispose() { foreach (var container in _containerByType.Values) container.Dispose(); } } public class EntityFrameworkContainer<TContainer> : IContainer<TContainer> where TContainer : ObjectContext, new() { private TContainer _container; public EntityFrameworkContainer() { _container = new TContainer(); } public IRepository<TEntity> GetRepository<TEntity>(Func<TContainer, ObjectQuery<TEntity>> repositorySelector) where TEntity : EntityObject { return new EntityFrameworkRepository<TContainer, TEntity>(_container, repositorySelector(_container)); } public void Dispose() { _container.Dispose(); } } public class EntityFrameworkRepository<TContainer, TEntity> : IRepository<TEntity> where TContainer : ObjectContext, new() where TEntity : EntityObject { private TContainer _container; private ObjectQuery<TEntity> _objectQuery; public EntityFrameworkRepository(TContainer container, ObjectQuery<TEntity> objectQuery) { _container = container; _objectQuery = objectQuery; } public IQueryable<TEntity> GetSatisfying(Expression<Func<TEntity, bool>> specification) { return _objectQuery.Where(specification); } public void Add(TEntity entity) { _container.AddObject(_objectQuery.Name, entity); } }
Analysis
This is the smallest implementation of the Repository pattern that I could come up with. It is obviously not feature complete. For example, it does not implement Delete, nor does it allow you to specify eager loading with Include. There are other implementations that are bigger, but I doubt that there could be one smaller. Even at this size, this doesn’t qualify as “all you need to do is”.
One benefit of the Repository pattern is supposed to be that it abstracts the persistence mechanism. But this implementation returns EntityObjects, which are a distinctly Entity Framework-ish data type. I could try for a POCO compliant implementation to solve that problem.
Because this implementation requires EntityObjects, it could never work with RIA Services. I would have to write a different Repository implementation to unit test my client code.
And finally, Entity Framework does some things that the in-memory repository does not. My unit tests can’t verify that I’m using EF correctly. For example, Entity Framework does eager loading if I explicitly request it. The in-memory implementation always has all navigation properties populated. Because of this difference, I can’t verify with a unit test that I have included all of the required navigations.
Conclusion
All of this leads me to conclude that the Repository pattern is not the best way to add testability to an untestable framework. The framework needs to be testable from the beginning. If Entity Framework had provided an in-memory implementation for testing, then I could test my use of EF, including eager loading.
By the way, Correspondence does in fact provide an in-memory implementation for unit testing. Please go through the lessons to see what I think a testable framework should look like.