IEnumerable и IQueryable для типов бизнес-логики или DAL - PullRequest
24 голосов
/ 13 июля 2011

Я знаю, что эти вопросы задавались ранее, я начну с перечисления нескольких из них (те, которые я читал до сих пор):

Как вы можете видетьЕсть несколько замечательных ресурсов по SO только на эту тему, но есть один вопрос / раздел вопроса, который я до сих пор не уверен, прочитав их все до конца.

Меня больше всего интересует IEnumerable vsIQueryable вопрос, а точнее, связь между DAL и потребителями .

Я нашел различные мнения относительно двух интерфейсов, которые были великолепны.Тем не менее, я обеспокоен последствиями возвращения DAL IQueryable.Насколько я понимаю, IQueryable предлагает / подразумевает, что под капотом находится провайдер Linq.Это проблема номер один - что, если DAL вдруг потребует данные из источника, не предоставленного Linq?Следующее будет работать, но это скорее хак?

public static IQueryable<Product> GetAll()
{
    // this function used to use a L2S context or similar to return data 
    // from a database, however, now it uses a non linq provider

    // simulate the non linq provider...
    List<Product> results = new List<Product> { new Product() };
    return results.AsQueryable();
}

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

Я мог бы исправить это, изменив возвращаемый результаттип функции для IEnumerable.Затем я могу вернуть IQueryable из функции, потому что она наследует IEnumerable, и я могу сохранить отложенную загрузку.Что я теряю, так это возможность добавлять к выражению запроса:

var results = SomeClass.GetAll().Where(x => x.ProductTypeId == 5);

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

Как другие люди обходят это?

  • Предоставить больше функций в DAL - GetAllByProductType, GetAllByStartDate, ... и т. Д.
  • Предоставить перегрузку, которая принимает предикаты?т.е.

    public static IEnumerable<Product> GetAll(Predicate<Product> predicate)
    {
    
        List<Product> results = new List<Product> { new Product() };
        return results.Where(x => predicate(x));
    }
    

Одна последняя часть (извините, я знаю, очень длинный вопрос!).

Я считаю, что IEnumerable является наиболее рекомендуемым во всех вопросах, которые япроверил, но как быть с требованием отсроченных загрузок для доступности текста данных?Насколько я понимаю, если ваша функция возвращает IEnumerable, но вы возвращаете IQueryable, IQueryable зависит от базового текста данных.Поскольку результат на этом этапе на самом деле является выражением и ничего не было занесено в память, вы не можете гарантировать, что потребитель DAL / функции будет выполнять запрос, ни когда.Так должен ли я сохранять экземпляр контекста, из которого были получены результаты, каким-то образом?Это как / почему шаблон единицы работы входит в игру?

Краткое изложение вопросов для ясности (выполнил поиск "?" ...):

  1. При использованииIQueryable в качестве типа возврата, слишком ли сильно вы связываете свой пользовательский интерфейс / бизнес-логику с провайдерами Linq?
  2. Является ли использование расширения AsQueryable () хорошей идеей, если вам вдруг нужно вернуть данные из источника, не предоставленного Linq
  3. У кого-нибудь есть хорошая ссылка, описывающая, как, например, преобразование стандартного списка в AsQueryable работает, что он на самом деле делает?
  4. Как вы справляетесь с дополнительными требованиями к фильтрации, предоставляемыми бизнес-логикой для вашего DAL?
  5. Кажется, что отложенная загрузка IEnumerable и IQueryable зависит от поддержки основного провайдера. Должен ли я использовать шаблон Unit of Work или что-то еще для этого?

Заранее большое спасибо!

1 Ответ

19 голосов
/ 13 июля 2011
  1. хорошо, вы не связаны строго с каким-либо конкретным провайдером, но, перефразируя это: вы не можете легко протестировать код, поскольку каждый провайдер имеет разные поддерживаемые функции(имеется в виду: то, что работает для одного, может не работать для другого - даже что-то вроде .Single())
  2. Я так не думаю, если у вас есть любой вопрос о том, чтобы когда-либо изменитьсяпровайдер - см. выше
  3. , он просто предоставляет оформленную обертку, которая использует .Compile() для любых лямбд, и вместо этого использует LINQ-to-Objects.Примечание. LINQ-to-Objects поддерживает на больше , чем любой другой провайдер, так что это не будет проблемой - за исключением того, что это означает, что любые "пародии", использующие этот подход, на самом деле не проверяют ваш реальный код вообще и в значительной степени бессмысленно (ИМО)
  4. да, сложно - см. Ниже
  5. да, сложно - см. Ниже

Лично яздесь предпочтительнее четко определенные API, которые принимают известные параметры и возвращают загруженные List<T> или IList<T> (или аналогичные) результаты;это дает вам тестируемый / макетируемый API и не оставляет вас во власти отложенного выполнения (ад закрытого соединения и т. д.).Это также означает, что любые различия между поставщиками обрабатываются внутри для реализации вашего уровня данных.Это также намного лучше подходит для сценариев вызова, таких как веб-сервисы и т. Д.

Короче говоря;учитывая выбор между IEnumerable<T> и IQueryable<T>, я выбираю ни один - вместо этого выбрав IList<T> или List<T>.Если мне понадобится дополнительная фильтрация, либо:

  1. Я добавлю это к существующему API через параметры и выполню фильтрацию внутри уровня данных
  2. Я приму это слишком большоеданные возвращаются, которые мне затем нужно отфильтровать при звонке
...