Мы разрабатываем приложение ASP.NET MVC и теперь создаем классы репозитория/службы. Мне интересно, есть ли какие-либо существенные преимущества для создания универсального интерфейса IRepository, который реализуется всеми репозиториями, причем каждый репозиторий имеет свой собственный уникальный интерфейс и набор методов.
Например: общий интерфейс IRepository может выглядеть (взятый из этого ответа):
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();
}
Каждый репозиторий будет реализовывать этот интерфейс, например:
- CustomerRepository: IRepository
- ProductRepository: IRepository
- и др.
Альтернативой, который мы выполнили в предыдущих проектах, будет:
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);
}
Во втором случае выражения (LINQ или иначе) будут полностью содержаться в реализации Репозитория, кто бы ни реализовал эту услугу, просто должен знать, какую функцию репозитория вызывать.
Я думаю, я не вижу преимущества написания всего синтаксиса выражения в классе службы и передачи в репозиторий. Разве это не означает, что во многих случаях дублирующийся код LINQ дублируется?
Например, в нашей старой системе выставления счетов мы называем
InvoiceRepository.GetSingleInvoice(DateTime invoiceDate, int accountId)
из нескольких различных сервисов (Клиент, Счет, Счет и т.д.). Это кажется намного более чистым, чем запись следующего в нескольких местах:
rep.GetSingle(x => x.AccountId = someId && x.InvoiceDate = someDate.Date);
Единственный недостаток, который я вижу в использовании конкретного подхода, состоит в том, что мы могли бы получить множество перестановок функций Get *, но это все же кажется предпочтительным для толкания логики выражения в классы Service.
Что мне не хватает?