Introduction
In many applications, business logic accesses data from data stores. There are lot of potential issues when accessing them directly, for example:
- Code duplication
- Higher chances of programming errors
- Problems in testing business logic in isolation from external dependencies
- Difficulty in implementing centrally managed consistent rules and logic
So it is a good idea to build a layer of abstraction which mediates between the business layer and data source layer of the application. This is where repository pattern comes in to achieve our objectives.
What is Repository
As described on
Martin Flower's website:
"A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers."
Implementation
We will discuss the implementation of repositories in context of
DDD(domain driven design) for entity framework. Using generics we will create a base repository which will have all the common functionality. And then we will inherit from the base repository to create model/domain specific repositories.
Repository Interface
This is how the repository interface will look like.
IRepository<T> is the contract where T is a POCO class. For testing we can later write an fake implementation to remove the dependency.
public interface IRepository<T> where T : class
{
IQueryable<T> GetQuery();
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);
void SaveChanges();
}
Generic Repository Implementation
The base repository takes an
IObjectContext as a constructor argument for dependency injection.
public abstract class EFRepositoryBase<T> : IRepository<T> where T : class
{
readonly IObjectContext _objectContext;
readonly IObjectSet<T> _objectSet;
public EFRepositoryBase(IObjectContext objectContext)
{
_objectContext = objectContext;
_objectSet = _objectContext.CreateObjectSet<T>();
}
public IQueryable<T> GetQuery()
{
return _objectSet;
}
public IEnumerable<T> GetAll()
{
return GetQuery().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);
}
public void SaveChanges()
{
_objectContext.SaveChanges();
}
}
Domain specific repository
In these derived repositories you can add the domain/model specific query operations. Here is how the interface and implementation of a User model repository will look like.
public interface IUserRepository : IRepository<User>
{
//add user specific operations
}
public class EFUserRepository : EFRepositoryBase<User>, IUserRepository
{
public EFUserRepository(IObjectContext objectContext)
: base(objectContext)
{
//add user specific operations
}
}