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

В каких случаях мне нужно создать два разных метода расширения для IEnumerable и IQueryable?

Скажем, мне нужен метод расширения, который выбирает только требуемые свойства из разных источников. Источником может быть база данных или сборка в памяти. Поэтому я определил такой метод расширения:

 public IQueryable<TResult> SelectDynamic<TResult>(
            this IQueryable<T> source,
            ...)

Это отлично работает для IQueryable s. Но я должен вызвать эту функцию также для IEnumerable s.

И в этом случае я могу вызвать его с помощью .AsQueryable():

  myEnumerable.AsQueryable()
        .SelectDynamic(...)
        .ToList();

Оба работают нормально. И у меня есть такой вопрос, если оба работают нормально, , в каких условиях я должен создать два разных метода расширения для одной и той же цели, поскольку один работает с IEnumerable, а другой с IQueryable?

Мой метод должен отправить запрос в базу данных в случае Queryable.

Например, здесь источник .Select метода расширения внутри пространства имен System.Linq:

Я снова повторяю свой основной вопрос:

Мой метод должен отправить запрос в базу данных в случае Queryable, но не при работе с IEnumerable. И пока я использую AsQueryable() для перечислений. Потому что я не хочу писать тот же код для Enumerable. Может ли он иметь некоторые побочные эффекты?

4b9b3361

Ответ 1

myEnumerable.AsQueryable() возвращает пользовательский объект: new EnumerableQuery<TElement>(myEnumerable); (исходный код)

Этот класс EnumerableQuery реализует IEnumerable<T> и IQueryable<T>

При использовании результата EnumerableQuery .AsQueryable() в качестве IEnumerable реализация метода интерфейса IEnumerable<T>.GetIterator() просто возвращает исходный итератор исходного кода, поэтому никаких изменений и минимальных издержек не происходит.

При использовании результата .AsQueryable() в качестве IQueriable реализация свойства интерфейса IQueriable.Expression просто возвращает Expression.Constant(this), готовый к оценке позже как IEnumerable, когда потребляется все дерево выражений.

(Все остальные методы и пути кода EnumerableQuery на самом деле не актуальны, когда EnumerableQuery сконструирован непосредственно из IEnumerable, насколько я могу судить)

Если вы правильно поняли, вы внедрили свой метод selectDynamic<TResult>() таким образом, чтобы вы построили дерево выражений внутри метода, что при компиляции дает желаемый результат.

Насколько я понимаю исходный код, когда вы звоните, например. myEnumerable.AsEnumerable().selectDynamic().ToList(), созданное вами дерево выражений компилируется и выполняется на myEnumerable, и суммарные накладные расходы должны быть довольно минимальными, так как вся эта работа выполняется только один раз для каждого запроса, а не один раз для каждого элемента.

Итак, я думаю, что нет ничего плохого в реализации вашего метода IEnumerable Extension, например:

public IEnumerable<TResult> SelectDynamic<TResult>(
        this IEnumerable<T> source,...)
    return source.AsQueryable().SelectDynamic();
}

Есть некоторые небольшие накладные расходы, так как это компилирует запрос один раз при каждом вызове этого метода, и я не уверен, что JITer достаточно умен, чтобы кэшировать эту компиляцию. Но я думаю, что это не будет заметно в большинстве случаев, если вы не выполните этот запрос тысячу раз в секунду.

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

Ответ 2

Если ваш код работает только тогда, когда объекты, с которыми он работает, загружен в память, просто поставьте вариант IEnumerable и позвольте своим абонентам решить, когда они хотят преобразовать IQueryable во встроенную память IEnumerable.

Как правило, вы не будете внедрять новые варианты вокруг IQueryable, если только вы не пишете нового поставщика базы данных.