Понимание.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.

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

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

4 ответа

Решение

Причиной AsEnumerable является

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

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

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

Частичное и частичное решение может быть способом. Рассматривать

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 перечисляется, запрашивается или используется иным образом, в базу данных будет передано как можно больше работы, которая вернется достаточно для продолжения работы. Предполагая, что действительно невозможно переписать, чтобы всю работу можно было отправить в базу данных, это может быть подходящим компромиссом.

Есть три реализации AsEnumerable,

DataTableExtensions.AsEnumerable

Расширяет 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 до Objects будет использоваться вместо реализации Query Provider.

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

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

Другие вопросы по тегам