Понимание .AsEnumerable() в LINQ to SQL

Учитывая следующий запрос LINQ to SQL:

var test = from i in Imports
           where i.IsActive
           select i;

Сформулированный оператор SQL:

SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1

Скажем, я хотел выполнить какое-то действие в select, который не может быть преобразован в SQL. Его понимание того, что обычный способ достичь этого состоит в том, чтобы сделать AsEnumerable() таким образом, превратив его в работоспособный объект.

Учитывая этот обновленный код:

var test = from i in Imports.AsEnumerable()
           where i.IsActive
           select new 
           { 
               // Make some method call 
           };

И обновленный SQL:

SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0] 

Обратите внимание на отсутствие предложения where в исполняемом операторе SQL.

Означает ли это, что вся таблица "Импорт" кэшируется в память? Будет ли эта медленная производительность вообще, если в таблице содержится большое количество записей?

Помогите мне понять, что на самом деле происходит за кулисами здесь.

Ответ 1

Причина AsEnumerable заключается в

AsEnumerable (TSource) (IEnumerable (TSource)) может использоваться для выбора между запросом реализации, когда последовательность реализует IEnumerable (T), но также имеет другой набор публичных запросов доступные методы

Итак, когда вы вызывали метод Where раньше, вы вызывали другой метод Where из IEnumerable.Where. Это предложение Where для LINQ для преобразования в SQL, новый Where is IEnumerable, который принимает IEnumerable, перечисляет его и дает соответствующие элементы. Это объясняет, почему вы видите, как генерируется другой SQL. Таблица будет взята полностью из базы данных до того, как расширение будет применяться в вашей второй версии кода. Это может создать серьезную шею бутылки, потому что весь стол должен быть в памяти, или, что еще хуже, вся таблица должна была бы перемещаться между серверами. Разрешить SQL-серверу выполнять "Куда" и делать то, что он делает лучше всего.

Ответ 2

В тот момент, когда перечисление перечислится, затем будет запрошена база данных и весь полученный результат.

Решение Part-Part может быть способом. Рассмотрим

var res = (
    from result in SomeSource
    where DatabaseConvertableCriterion(result)
    && NonDatabaseConvertableCriterion(result)
    select new {result.A, result.B}
);

Скажем также, что для NonDatabaseConvertableCriterion требуется поле C из результата. Поскольку NonDatabaseConvertableCriterion делает то, что предлагает его название, это должно выполняться как перечисление. Однако учтите:

var partWay =
(
    from result in SomeSource
    where DatabaseConvertableCriterion(result)
    select new {result.A, result.B, result.C}
);
var res =
(
    from result in partWay.AsEnumerable()
    where NonDatabaseConvertableCriterion select new {result.A, result.B}
);

В этом случае, когда res перечисляется, запрашивается или иным образом используется, как можно больше работы будет передано в базу данных, которая вернет достаточно, чтобы продолжить работу. Предполагая, что действительно невозможно переписать, чтобы вся работа могла быть отправлена ​​в базу данных, это может быть подходящим компромиссом.

Ответ 3

Существует три реализации AsEnumerable.

DataTableExtensions.AsEnumerable

Расширяет a DataTable, чтобы дать ему интерфейс IEnumerable, чтобы вы могли использовать Linq для DataTable.

Enumerable.AsEnumerable<TSource> и ParallelEnumerable.AsEnumerable<TSource>

Метод AsEnumerable<TSource>(IEnumerable<TSource>) не действует кроме того, чтобы изменить тип источника времени компиляции из типа, который реализует IEnumerable<T> до IEnumerable<T>.

AsEnumerable<TSource>(IEnumerable<TSource>) можно использовать для выбора между реализациями запросов, когда последовательность реализует IEnumerable<T>, но также имеет другой набор методов публичных запросов доступный. Например, учитывая общий класс Table, который реализует IEnumerable<T> и имеет свои собственные методы, такие как Where, Select и SelectMany, вызов Where вызовет общедоступный метод Where Table. Тип Table, который представляет таблицу базы данных, может иметь Where, который принимает предикатный аргумент как дерево выражений и преобразует дерево в SQL для удаленного выполнения. Если удаленное выполнение не желательно, например, потому что предикат вызывает локальный метод, метод AsEnumerable<TSource> может быть использован, чтобы скрыть настраиваемые методы и вместо этого сделать стандартные операторы запроса доступны.

Другими словами.

Если у меня есть

IQueryable<X> sequence = ...;

от LinqProvider, например Entity Framework, и я,

sequence.Where(x => SomeUnusualPredicate(x));

этот запрос будет составлен и запущен на сервере. Это не удастся во время выполнения, потому что EntityFramework не знает, как преобразовать SomeUnusualPredicate в SQL.

Если я хочу, чтобы вместо этого выполнялся оператор с Linq to Objects,

sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));

теперь сервер вернет все данные, и вместо реализации запроса поставщика будет использоваться Enumerable.Where из Linq to Objects.

Не имеет значения, что Entity Framework не знает, как интерпретировать SomeUnusualPredicate, моя функция будет использоваться напрямую. (Тем не менее, это может быть неэффективный подход, поскольку все строки будут возвращены с сервера.)

Ответ 4

Я считаю, что AsEnumerable просто сообщает компилятору, какие методы расширения использовать (в этом случае те, которые определены для IEnumerable, а не для IQueryable). Выполнение запроса все еще отложено до тех пор, пока вы не назовете ToArray или не перечислите его.