We are developing an ASP.NET MVC application, and are now building the repository/service classes. I'm wondering if there are any major advantages to creating a generic IRepository interface that all repositories implement, vs. each Repository having its own unique interface and set of methods.
For example: a generic IRepository interface might look like (taken from this answer):
public interface IRepository : IDisposable
{
T[] GetAll<T>();
T[] GetAll<T>(Expression<Func<T, bool>> filter);
T GetSingle<T>(Expression<Func<T, bool>> filter);
T GetSingle<T>(Expression<Func<T, bool>> filter, List<Expression<Func<T, object>>> subSelectors);
void Delete<T>(T entity);
void Add<T>(T entity);
int SaveChanges();
DbTransaction BeginTransaction();
}
Each Repository would implement this interface, for example:
- CustomerRepository:IRepository
- ProductRepository:IRepository
- etc.
The alternate that we've followed in prior projects would be:
public interface IInvoiceRepository : IDisposable
{
EntityCollection<InvoiceEntity> GetAllInvoices(int accountId);
EntityCollection<InvoiceEntity> GetAllInvoices(DateTime theDate);
InvoiceEntity GetSingleInvoice(int id, bool doFetchRelated);
InvoiceEntity GetSingleInvoice(DateTime invoiceDate, int accountId); //unique
InvoiceEntity CreateInvoice();
InvoiceLineEntity CreateInvoiceLine();
void SaveChanges(InvoiceEntity); //handles inserts or updates
void DeleteInvoice(InvoiceEntity);
void DeleteInvoiceLine(InvoiceLineEntity);
}
In the second case, the expressions (LINQ or otherwise) would be entirely contained in the Repository implementation, whoever is implementing the service just needs to know which repository function to call.
I guess I don't see the advantage of writing all the expression syntax in the service class and passing to the repository. Wouldn't this mean easy-to-messup LINQ code is being duplicated in many cases?
For example, in our old invoicing system, we call
InvoiceRepository.GetSingleInvoice(DateTime invoiceDate, int accountId)
from a few different services (Customer, Invoice, Account, etc). That seems much cleaner than writing the following in multiple places:
rep.GetSingle(x => x.AccountId = someId && x.InvoiceDate = someDate.Date);
The only disadvantage I see to using the specific approach is that we could end up with many permutations of Get* functions, but this still seems preferable to pushing the expression logic up into the Service classes.
What am I missing?
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.
Have a generic repository that is wrapped by a specific repository. That way you can control the public interface but still have the advantage of code-reuse that comes from have a generic repository.
public class UserRepository : Repository, IUserRepository
Shouldn't you inject IUserRepository to avoid exposing the interface. As people have said, you may not need the full CRUD stack etc.