Понимание.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 или не перечислите его.