I am writing an EF4 datalayer for an MVC 2 web application and I need suggestions on choosing inheritance vs. abstract base classes. My repository has worked well following the ‘generic repo’ structure but now I want to add “Audit” functionality which records everytime a CRUD operation is performed.
This is the contract I’ve been using so far:
public interface IRepository<T>
{
void Create(T entity);
void Update(T entity);
void Delete(Func<T, bool> predicate);
T Get(Func<T, bool> predicate);
IQueryable<T> Query();
}
My repo. implementation looks like this:
sealed class EFRepository<TEntity> : IRepository<TEntity>
where TEntity : EntityObject
{
ObjectContext _context;
ObjectSet<TEntity> _entitySet;
public EFRepository(ObjectContext context)
{
_context = context;
_entitySet = _context.CreateObjectSet<TEntity>();
}
public void Create(TEntity entity)
{
_entitySet.AddObject(entity);
_context.SaveChanges();
}
public void Update(TEntity entity)
{
_entitySet.UpdateObject(entity);
_context.SaveChanges();
}
public void Delete(Func<TEntity, bool> predicate)
{
TEntity entity = _entitySet.Single(predicate);
_entitySet.DeleteObject(entity);
_context.SaveChanges();
}
public TEntity Get(Func<TEntity, bool> predicate)
{
return _entitySet.SingleOrDefault(predicate);
}
public IQueryable<TEntity> Query()
{
return _entitySet;
}
}
I want to create the concept of an AuditableRepository<T>. Should I create it like this:
interface IAuditable<T>
interface IRepository<T>
AuditableRepository<T> : IRepository<T>, IAuditable<T>
EFRepository<T> : AuditableRepository<T>
or is it better to have it like this:
interface IAuditable<T>
interface IRepository<T>
EFRepository<T> : IRepository<T>, IAuditable<T>
or even:
interface IAuditable<T>
interface IRepository<T>
AuditableRepository<T> : IRepository<T>, IAuditable<T>
EFRepository<T> : IRepository<T>
AuditableEFRepository<T> : AuditableRepository<T>
Not all of my EFRepositories will need to be audited. How should I proceed?
Here is another possibility (using a Decorator object to add additional functionality to an existing repository):
The advantage to using a Decorator to add additional features is that it avoids the combinatorial explosion you began to see when you considered some repositories with auditing, some without, some using EF, some not. This gets progressively worse with every new feature that may or may not apply, often eventually devolving into configuration flags and messy internal branching.