Advantage of creating a generic repository vs. specific repository for each object? Advantage of creating a generic repository vs. specific repository for each object? asp.net asp.net

Advantage of creating a generic repository vs. specific repository for each object?


This is an issue as old as the Repository pattern itself. The recent introduction of LINQ's IQueryable, a uniform representation of a query, has caused a lot of discussion about this very topic.

I prefer specific repositories myself, after having worked very hard to build a generic repository framework. No matter what clever mechanism I tried, I always ended up at the same problem: a repository is a part of the domain being modeled, and that domain is not generic. Not every entity can be deleted, not every entity can be added, not every entity has a repository. Queries vary wildly; the repository API becomes as unique as the entity itself.

A pattern I often use is to have specific repository interfaces, but a base class for the implementations. For example, using LINQ to SQL, you could do:

public abstract class Repository<TEntity>{    private DataContext _dataContext;    protected Repository(DataContext dataContext)    {        _dataContext = dataContext;    }    protected IQueryable<TEntity> Query    {        get { return _dataContext.GetTable<TEntity>(); }    }    protected void InsertOnCommit(TEntity entity)    {        _dataContext.GetTable<TEntity>().InsertOnCommit(entity);    }    protected void DeleteOnCommit(TEntity entity)    {        _dataContext.GetTable<TEntity>().DeleteOnCommit(entity);    }}

Replace DataContext with your unit-of-work of choice. An example implementation might be:

public interface IUserRepository{    User GetById(int id);    IQueryable<User> GetLockedOutUsers();    void Insert(User user);}public class UserRepository : Repository<User>, IUserRepository{    public UserRepository(DataContext dataContext) : base(dataContext)    {}    public User GetById(int id)    {        return Query.Where(user => user.Id == id).SingleOrDefault();    }    public IQueryable<User> GetLockedOutUsers()    {        return Query.Where(user => user.IsLockedOut);    }    public void Insert(User user)    {        InsertOnCommit(user);    }}

Notice the public API of the repository does not allow users to be deleted. Also, exposing IQueryable is a whole other can of worms - there are as many opinions as belly buttons on that topic.


I actually disagree slightly with Bryan's post. I think he's right, that ultimately everything is very unique and so on. But at the same time, most of that comes out as you design, and I find that getting a generic repository up and using it while developing my model, I can get an app up very quickly, then refactor to greater specificity as I find the need to do so.

So, in cases like that, I have often created a generic IRepository that has the full CRUD stack, and that lets me get quickly to playing with the API and letting folks play w/ the UI and do integration & user acceptance testing in parallel. Then, as I find I need specific queries on the repo, etc, I start replacing that dependency w/ the specific one if needed and going from there. One underlying impl. is easy to create and use (and possibly hook to an in-memory db or static objects or mocked objects or whatever).

That said, what I have started doing lately is breaking up the behavior. So, if you do interfaces for IDataFetcher, IDataUpdater, IDataInserter, and IDataDeleter (for example) you can mix-and-match to define your requirements through the interface and then have implementations that take care of some or all of them, and I can still inject the does-it-all implementation to use while I'm building the app out.

paul


I prefer specific repositories which derives from generic repository (or list of generic repositories to specify exact behavior) with overridable method signatures.