Понимание .AsEnumerable () в LINQ to SQL - PullRequest
44 голосов
/ 22 июля 2010

Учитывая следующий запрос 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

Скажем, я хотел выполнить какое-то действие в выборе, которое нельзя преобразовать в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 ]

34 голосов
/ 22 июля 2010

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

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

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

6 голосов
/ 23 июля 2010

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

Частичное и частичное решение может быть подходящим.Рассмотрим

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

5 голосов
/ 19 мая 2014

Существует три реализации 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.

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

2 голосов
/ 22 июля 2010

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

...