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 (e.g. 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?