Подтвердить что ты не робот

Должен ли репозиторий возвращать IEnumerable <T>, IQueryable <T> или List <T>?

Я хотел бы сделать мое приложение максимально гибким, но не выкопать себя в отверстие, сделав мой интерфейс слишком конкретным.

Каков наилучший тип объекта для репозитория? IEnumerable, IQueryable или List?

Технологии, которые я рассматриваю, - это

  • Кэширование приложений Azure App

  • Entity Framework 4.1

  • Возможно, Windows Server AppFabric

4b9b3361

Ответ 1

Я бы сказал, постройте свой DAL, используя IQueryable, и передайте его, убедитесь, что ваш объект соответствует жизни - это запрос. Таким образом, вы получите преимущество отсроченного выполнения, но подвержены риску неэффективного запроса базы данных.

Затем убедитесь, что ваша производительность протестировала ваше приложение (или, по крайней мере, части, которые, скорее всего, получили трафик), и просмотрите образцы данных. Создайте специализированные методы в вашем DAL, чтобы получить полностью материализованные onjects и сделать эти запросы предварительно запрограммированными запросами.

образец интерфейса репозитория будет похож на

  public interface IDataContext
  {
        void Add<T>(T entity) where T : BaseEntity;
        void Delete<T>(T entity) where T : BaseEntity;
        IQueryable<T> Find<T>(Expression<Func<T, bool>> where) where T : BaseEntity;
   }

где BaseEntity является базовым классом для всех наших классов, похоже, этот класс не сопоставляется ни с одной таблицей в db

public abstract class BaseEntity
{
        public int Id { get; set; }
        public DateTime CreateDateTime { get; set; }
        public string CreateUser { get; set; }
        public DateTime ModDateTime { get; set; }
        public string ModUser { get; set; }
        public byte[] RowVersion { get; set; }
}

Expression<Func<T, bool>> передаст все выражение в ваш репозиторий, а не только Func, так как EF работает над выражением для генерации SQL-запроса, типичным будет использование

ICollection<WFGroup> wgGroups = this.dataContext.Find<WFGroup>((w) => true).ToList();

где WFGroup - это класс, полученный из BaseEntity, я обычно использую ленивую загрузку и прокси и не отсоединяю/присоединяю объекты к контексту.

Ответ 2

Это зависит от того, хотите ли вы, чтобы какие-либо будущие запросы выполнялись над сущностью и должны ли они быть в памяти или нет:

  • Если есть будущие запросы, и БД должна выполнить работу, возвращаемую IQueryable.
  • Если есть будущие запросы, и это должно быть сделано в памяти, возвращайте IEnumerable.
  • Если дальнейших запросов не будет, и все данные должны быть прочитаны, верните IList, ICollection и т.д.

Ответ 3

Насколько вероятно, вам когда-либо понадобится вернуть пользовательскую реализацию IEnumerable (а не сборку) из вашего DAL? (Чтобы ответить на этот вопрос, посмотрите на свои предыдущие проекты и подсчитайте, сколько из них или yield return у вас есть.)

Если ответ "не очень", я бы просто вернул ICollection или даже массивы (если вы хотите, чтобы результаты запроса были непреднамеренно изменены). В крайнем случае, если вам когда-либо понадобится изменить запрос для "потока" результатов с пользовательским IEnumerable, вы всегда можете использовать старый метод для вызова нового и материализовать результаты, чтобы поддерживать совместимость со старыми клиентами.

Ответ 4

В этой статье есть очень хорошая недавняя статья которая охватывает это, а именно под заголовком "Хранилища, возвращающие IQueryable" . Вот что он говорит:

Одной из причин использования шаблона репозитория является инкапсуляция жирных запросов. Эти запросы затрудняют чтение, понимание и проверку действий в ASP.NET MVC-контроллерах. Кроме того, по мере роста вашего приложения шансы на повторение жирного запроса в разных местах возрастают. С шаблоном репозитория мы инкапсулируем эти запросы в классы репозитория. Результат - более тонкие, более чистые, более удобные и простые в проверке действия. Рассмотрим этот пример:

var orders = context.Orders
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

Здесь мы напрямую используем DbContext без шаблона репозитория. Когда ваши методы репозитория возвращают IQueryable, кто-то собирается получить этот IQueryable и составить запрос поверх него. Вот результат:

var orders = repository.GetOrders()
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

Вы видите разницу между этими двумя фрагментами кода? Единственная разница в первой строке. В первом примере мы используем context.Orders, во втором мы используем repository.GetOrders(). Итак, какова проблема в решении этого репозитория? Ничего!

Ваши репозитории должны возвращать объекты домена. Таким образом, метод GetOrders() должен возвращать IEnumerable. При этом второй пример можно переписать следующим образом:

var orders = repository.GetOrders(1234);

См. разницу?

В результате этого я добавил в свою команду следующее кодирование:

Для методов класса репозитория никогда не возвращайте объект IQueryable. Всегда сначала перечисляйте или конвертируйте его (например, ToArray, ToList, AsEnumerable).

Причиной является то, что IQueryable позволит вызывающему абоненту построить на нем и в конечном итоге изменить SQL-запрос, который выполняется в базе данных. Это может быть опасно с точки зрения производительности БД, но это больше о SoC. Вызывающий не заботится об источнике данных; он просто хочет данные.