Как выполнить агрегированные операции с помощью шаблона репозитория?

Я видел различные сообщения в блоге (и много противоречивых советов) о шаблоне репозитория, и поэтому я начну с того, что приведенный ниже код, вероятно, не соответствует шаблону репозитория во многих мнениях людей. Тем не менее, это общепринятая реализация, и независимо от того, придерживается ли она оригинального определения Fowler или нет, мне все еще интересно узнать больше о том, как эта реализация используется на практике.

Предположим, что у меня есть проект, в котором доступ к данным абстрагируется с помощью интерфейса, такого как ниже, который предоставляет базовые операции CRUD.

public interface IGenericRepository<t>
{
 void Add(T entity);
 void Remove(T entity);
 void Update(T entity);
 IEnumerable<t> Fetch(Expression<func<t,bool>> where);
}
</func<t,bool></t></t>

Далее предположим, что у меня есть служебный уровень, построенный поверх этого, например:

public class FooService
{
 private IGenericRepository<foo> _fooRespository;
 ...
 public IEnumerable<foo> GetBrightlyColoredFoos()
 {
 return _fooRepository.Fetch(f => f.Color == "pink" || f.Color == "yellow");
 }
}
</foo></foo>

Теперь предположим, что мне теперь нужно знать, сколько ярких цветных Foo есть, даже не желая перечислять их. В идеале я хочу реализовать метод CountBrightlyColoredFoos() в моей службе, но реализация репозитория не дает мне возможности достичь этого, кроме как путем их извлечения и подсчета, что потенциально очень неэффективно.

Я мог бы расширить репозиторий, чтобы добавить метод Count(), но как насчет других агрегатных функций, которые могут мне понадобиться, например Min() или Max(), или Sum(), или... вы получаете идею.

Аналогично, что, если бы я хотел получить список различных цветов Foo (SELECT DISTINCT). Опять же, простой репозиторий не дает возможности делать такие вещи.

Хранение репозитория просто, чтобы его было легко протестировать/макетировать, очень похвально, но как вы тогда решаете эти требования? Разумеется, есть только два пути: более сложный репозиторий или "back-door" для уровня сервиса, который используется в обход репозитория (и, таким образом, побеждает его цель).

1 ответ

Я бы сказал, вам нужно изменить свой дизайн. То, что вы хотите сделать, это иметь один "основной" общий репозиторий с базовым CRUD, но также и меньшими репозиториями для каждого объекта. Затем вам просто нужно нарисовать строку, где можно разместить определенные операции (например, сумма, счет, макс и т.д.). Скорее всего, не все ваши сущности должны будут подсчитываться, суммироваться и т.д., И большую часть времени вы не сможет добавить общую версию, которая применяется ко всем объектам для агрегатных функций.

Базовый репозиторий:

public abstract class BaseRep<t> : ********<t> where T : class
{
 //basic CRUD
}
</t></t>

Репозиторий Foo:

public class FooRep : BaseRep<foo>, IFooRep
{
 //foo specific functions
}
</foo>

licensed under cc by-sa 3.0 with attribution.