Лямбда выражение с телом оператора не может быть преобразовано

Обновлено: 07.07.2024

In using the EntityFramework, I get the error " A lambda expression with a statement body cannot be converted to an expression tree " when trying to compile the following code:

I don't know what the error means and most of all how to fix it. Any help?

11 Answers 11

Is objects a Linq-To-SQL database context? In which case, you can only use simple expressions to the right of the => operator. The reason is, these expressions are not executed, but are converted to SQL to be executed against the database. Try this

You can use statement body in lamba expression for IEnumerable collections. try this one:

Notice:
Think carefully when using this method, because this way, you will have all query result in memory, that may have unwanted side effects on the rest of your code.

@DatVM it depends on what you are going to do. this can not be always right choice and of course can not be always wrong choice.

Though I agree with you, the OP stated that he was using EntityFramework. Most of the case, when working with EF, you want the database-side to do as much work as possible. It would be nice if you note the case in your answer.

It means that you can't use lambda expressions with a "statement body" (i.e. lambda expressions which use curly braces) in places where the lambda expression needs to be converted to an expression tree (which is for example the case when using linq2sql).

@vbullinger you're right to a degree, but in a more general sense (outside the context of linq-to-sql) this is a more direct answer. It helped me with an AutoMapper error

Without knowing more about what you are doing (Linq2Objects, Linq2Entities, Linq2Sql?), this should make it work:

When using this technique, I like to select the fields I will use into an anonymous type before calling .AsEnumerable()

The LINQ to SQL return object were implementing IQueryable interface. So for Select method predicate parameter you should only supply single lambda expression without body.

This is because LINQ for SQL code is not execute inside program rather than on remote side like SQL server or others. This lazy loading execution type were achieve by implementing IQueryable where its expect delegate is being wrapped in Expression type class like below.

Expression tree do not support lambda expression with body and its only support single line lambda expression like var col => col.id );

So if you try the following code won't works.

The following will works as per expected.

Use this overload of select:

This works for me but when used with Entity Framework would this solution prevent the dbcontext from loading all rows into memory first, like AsEnumerable() would?

9 years too late to the party, but a different approach to your problem (that nobody has mentioned?):

The statement-body works fine with Func<> but won't work with Expression> . IQueryable.Select wants an Expression<> , because they can be translated for Entity Framework - Func<> can not.

So you either use the AsEnumerable and start working with the data in memory (not recommended, if not really neccessary) or you keep working with the IQueryable<> which is recommended. There is something called linq query which makes some things easier:

with let you can define a variable and use it in the select (or where . ) - and you keep working with the IQueryable until you really need to execute and get the objects.

Является ли objects контекстом базы данных Linq-To-SQL? В этом случае вы можете использовать только простые выражения справа от оператора = > . Причина в том, что эти выражения не выполняются, а преобразуются в SQL, которые должны выполняться в отношении базы данных.
Попробуйте это

Вы можете использовать тело оператора в выражении lamba для коллекций IEnumerable.
попробуйте следующее:

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

Это означает, что вы не можете использовать лямбда-выражения с “телом оператора” (т.е. лямбда-выражения, которые используют фигурные скобки) в местах, где выражение лямбда должно быть преобразовано в дерево выражений (это, например, случай, когда используя linq2sql).

Не зная больше о том, что вы делаете (Linq2Objects, Linq2Entities, Linq2Sql?), это должно заставить его работать:

Используйте эту перегрузку для выбора:

Это означает, что выражение Lambda типа TDelegate , которое содержит ([parameters]) => < some code >; , не может быть преобразовано в Expression . Это правило.

Упростите свой запрос. Тот, который вы предоставили, может быть переписан следующим образом и будет скомпилирован:

Является ли Arr базовым типом Obj ? Существует ли класс Obj? Ваш код будет работать только в том случае, если Arr является базовым типом Obj. Вместо этого вы можете попробовать:

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

Объект возврата LINQ to SQL реализовал интерфейс IQueryable . Таким образом, для параметра предиката метода Select вы должны указывать только одно лямбда-выражение без тела.

Это связано с тем, что код LINQ для SQL не выполняется внутри программы, а не на удаленной стороне, такой как SQL-сервер или другие. Этот ленивый тип выполнения загрузки был достигнут путем реализации IQueryable, где его ожидаемый делегат обернут в класс типа выражения, как показано ниже.

Дерево выражений не поддерживает выражение лямбда с телом и единственное выражение lambda одной линии, например var col => col.id );

задан 03 марта '11, 07:03

попробуйте преобразовать в список, подобный этому. objects.List (). Select (. - nelson eldoro

10 ответы

Is objects контекст базы данных Linq-To-SQL? В этом случае вы можете использовать только простые выражения справа от оператора =>. Причина в том, что эти выражения не выполняются, а преобразуются в SQL для выполнения в базе данных. Попробуй это

Вы можете использовать тело оператора в выражении lamba для IEnumerable коллекции. Попробуй это:

Обратите внимание:
Тщательно подумайте при использовании этого метода, потому что в этом случае все результаты запроса будут в памяти, что может иметь нежелательные побочные эффекты для остальной части вашего кода.


+1 Мне это нравится! Добавление AsEnumerable() маскирует мою проблему, уйди! - Joel

Это реальное решение, принятый ответ трудно применить в некоторых случаях - Ферран Сальгуэро

@DatVM это зависит от того, что вы собираетесь делать. это не всегда может быть правильный выбор и, конечно, не всегда может быть неправильный выбор. - Амир Овейси

Хотя я согласен с вами, OP заявил, что он использует EntityFramework. В большинстве случаев при работе с EF вы хотите, чтобы сторона базы данных выполняла как можно больше работы. Было бы неплохо, если бы вы отметили в своем ответе случай. - Люк Во

ответ дан 03 мар '11, в 10:03

Вы . немного перефразировали ошибку. Ответ @Tim Rogers был намного лучше - Vbullinger

@vbullinger в какой-то степени вы правы, но в более общем смысле (вне контекста linq-to-sql) это более прямой ответ. Это помогло мне с ошибкой AutoMapper - mlhDev

vbullinger: Но мне это помогло. - Пол

vbullinger: У меня эта ошибка не связана с Linq-To-SQL. - Дирк Р

Не зная больше о том, что вы делаете (Linq2Objects, Linq2Entities, Linq2Sql?), Это должно заставить его работать:

ответ дан 03 мар '11, в 10:03


Это заставляет запрашиваемый оценить. - умный пещерный человек

Однако в этом случае это нормально, потому что он все равно вызывает ToArray () сразу после этого. - умный пещерный человек

не обязательно - кто знает, насколько велико "о"? у него может быть 50 свойств, когда все, что нам нужно, - это 2. - kdawg

При использовании этого метода мне нравится выбирать поля, которые я буду использовать, в анонимный тип перед вызовом .AsEnumerable() - Блейк Митчелл

Объект возврата LINQ to SQL реализовывал IQueryable интерфейс. Таким образом, для Select Параметр предиката метода вы должны указать только одно лямбда-выражение без тела.

Это связано с тем, что код LINQ для SQL не выполняется внутри программы, а не на удаленной стороне, такой как SQL-сервер или другие. Этот тип выполнения с отложенной загрузкой был достигнут путем реализации IQueryable, в котором ожидаемый делегат заключен в класс типа Expression, как показано ниже.

Дерево выражений не поддерживает лямбда-выражение с телом и поддерживает только однострочное лямбда-выражение, например var col => col.id );

Поэтому, если вы попробуете, следующий код не сработает.

Следующее будет работать, как ожидалось.


Используйте эту перегрузку select:

ответ дан 21 апр.


Это работает для меня, но при использовании с Entity Framework будет ли это решение препятствовать тому, чтобы dbcontext сначала загружал все строки в память, как это сделал бы AsEnumerable ()? - парламент

@par Parliament: чтобы предотвратить загрузку всех строк в память, вы должны использовать Expression> . - Мохсен

На 9 лет опоздали на вечеринку, но другой подход к вашей проблеме (о котором никто не упомянул?):

Заявление-тело отлично работает с Func<> но не будет работать с Expression> . IQueryable.Select хочет Expression<> , потому что они могут быть переведены для Entity Framework - Func<> не могу.

Так что вы либо используете AsEnumerable и начинаете работать с данными в памяти (не рекомендуется, если вообще не требуется) или продолжаете работать с IQueryable<> что рекомендуется. Есть что-то под названием linq query что упрощает некоторые вещи:

с let вы можете определить переменную и использовать ее в select (или where , . ) - и вы продолжаете работать с IQueryable пока вам действительно не понадобится выполнить и получить объекты.

После этого вы можете Obj[] myArray = result.ToArray()

Создан 22 июля '20, 12:07


@TeaBaerd, ха-ха, да. : D совпадения бывают забавными . - Матиас Бургер

Это означает, что лямбда-выражение типа TDelegate который содержит ([parameters]) => < some code >; не может быть преобразован в Expression . Это правило.

Упростите свой запрос. Тот, который вы предоставили, можно переписать следующим образом, и он будет скомпилирован:

ответ дан 03 мар '11, в 10:03

Is Arr базовый тип Obj ? Класс Obj существует? Ваш код будет работать, только если Arr является базовым типом Obj. Вместо этого вы можете попробовать следующее:

ответ дан 03 мар '11, в 10:03

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

ответ дан 27 окт '15, 19:10

Лямбда-выражение используется для создания анонимной функции. Используйте оператор объявления лямбда-выражения для отделения списка параметров лямбда-выражения от исполняемого кода. Лямбда-выражение может иметь одну из двух следующих форм:

Лямбда выражения, имеющая выражение в качестве текста:

Лямбда оператора, имеющая блок операторов в качестве текста:

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

Лямбда-выражение может быть преобразовано в тип делегата. Тип делегата, в который может быть преобразовано лямбда-выражение, определяется типами его параметров и возвращаемым значением. Если лямбда-выражение не возвращает значение, оно может быть преобразовано в один из типов делегата Action ; в противном случае его можно преобразовать в один из типов делегатов Func . Например, лямбда-выражение, которое имеет два параметра и не возвращает значение, можно преобразовать в делегат Action . Лямбда-выражение, которое имеет два параметра и возвращает значение, можно преобразовать в делегат Func . В следующем примере лямбда-выражение x => x * x , которое указывает параметр с именем x и возвращает значение x в квадрате, присваивается переменной типа делегата:

Лямбда-выражения можно также преобразовать в типы дерева выражения, как показано в следующем примере:

При использовании синтаксиса на основе методов для вызова метода Enumerable.Select в классе System.Linq.Enumerable (например, в LINQ to Objects и LINQ to XML) параметром является тип делегата System.Func . При вызове метода Queryable.Select в классе System.Linq.Queryable (например, в LINQ to SQL) типом параметра является тип дерева выражения Expression> . В обоих случаях можно использовать одно и то же лямбда-выражение для указания значения параметра. Поэтому оба вызова Select выглядят одинаково, хотя на самом деле объект, созданный из лямбда-выражения, имеет другой тип.

Выражения-лямбды

Лямбда-выражение с выражением в правой => части оператора называется => . Выражения-лямбды возвращают результат выражения и принимают следующую основную форму.

Лямбды операторов

Лямбда-инструкция напоминает лямбда-выражение, за исключением того, что инструкции заключаются в фигурные скобки:

Тело лямбды оператора может состоять из любого количества операторов; однако на практике обычно используется не более двух-трех.

Лямбда-инструкции нельзя использовать для создания деревьев выражений.

Если лямбда-выражение имеет только один входной параметр, круглые скобки необязательны:

Два и более входных параметра разделяются запятыми:

Иногда компилятор не может вывести типы входных параметров. Вы можете указать типы данных в явном виде, как показано в следующем примере:

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

Параметры пустой переменной лямбда-выражения полезны, если вы используете лямбда-выражение для указания обработчика событий.

Если только один входной параметр имеет имя _ , для обеспечения обратной совместимости _ рассматривается как имя этого параметра в лямбда-выражении.

Асинхронные лямбда-выражения

С помощью ключевых слов async и await можно легко создавать лямбда-выражения и операторы, включающие асинхронную обработку. Например, в следующем примере Windows Forms содержится обработчик событий, который вызывает асинхронный метод ExampleMethodAsync и ожидает его.

Такой же обработчик событий можно добавить с помощью асинхронного лямбда-выражения. Чтобы добавить этот обработчик, поставьте модификатор async перед списком параметров лямбда-выражения, как показано в следующем примере:

Дополнительные сведения о создании и использовании асинхронных методов см. в разделе Асинхронное программирование с использованием ключевых слов Async и Await.

Лямбда-выражения и кортежи

Кортеж определяется путем заключения в скобки списка его компонентов с разделителями-запятыми. В следующем примере кортеж с тремя компонентами используется для передачи последовательности чисел в лямбда-выражение. Оно удваивает каждое значение и возвращает кортеж с тремя компонентами, содержащий результат операций умножения.

Как правило, поля кортежи именуются как Item1 , Item2 и т. д. Тем не менее кортеж с именованными компонентами можно определить, как показано в следующем примере:

Лямбда-выражения со стандартными операторами запросов

Экземпляр этого делегата можно создать как Func , где int — входной параметр, а bool — возвращаемое значение. Возвращаемое значение всегда указывается в последнем параметре типа. Например, Func определяет делегат с двумя входными параметрами, int и string , и типом возвращаемого значения bool . Следующий делегат Func при вызове возвращает логическое значение, которое показывает, равен ли входной параметр 5:

В этом примере используется стандартный оператор запроса Count:

Компилятор может вывести тип входного параметра ввода; но его также можно определить явным образом. Данное лямбда-выражение подсчитывает указанные целые значения ( n ), которые при делении на два дают остаток 1.

В следующем примере кода показано, как создать последовательность, которая содержит все элементы массива numbers , предшествующие 9, так как это первое число последовательности, не удовлетворяющее условию:

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

Лямбда-выражения не используются непосредственно в выражениях запросов, но их можно использовать в вызовах методов в выражениях запросов, как показано в следующем примере:

Определение типа в лямбда-выражениях

Общие правила определения типа для лямбда-выражений формулируются следующим образом:

  • лямбда-выражение должно содержать то же число параметров, что и тип делегата;
  • каждый входной параметр в лямбда-выражении должен быть неявно преобразуемым в соответствующий параметр делегата;
  • возвращаемое значение лямбда-выражения (если таковое имеется) должно быть неявно преобразуемым в возвращаемый тип делегата.

Естественный тип лямбда-выражения

Лямбда-выражение в самом себе не имеет типа, так как система общих типов не имеет встроенного понятия "лямбда-выражение". Однако иногда бывает удобно говорить о неформальном "типе" лямбда-выражения. Под неофициальным термином "тип" понимается тип делегата или тип Expression, в который преобразуется лямбда-выражение.

Компилятор может определить parse как Func . Компилятор использует доступный делегат Func или Action , если он существует. Если нет, компилятор синтезирует тип делегата. Например, тип делегата синтезируется, если лямбда-выражение имеет параметры ref . Если лямбда-выражение имеет естественный тип, его можно присвоить менее явному типу, например System.Object или System.Delegate:

Группы методов (то есть имена методов без списков параметров) с ровно одной перегрузкой имеют естественный тип:

Не у всех лямбда-выражений есть естественный тип. Рассмотрим следующее объявление:

Компилятор не может определить тип параметра для s . Если компилятор не может определить естественный тип, необходимо объявить тип:

Явный тип возвращаемого значения

Как правило, тип возвращаемого значения лямбда-выражения является очевидным и легко выводится. Для некоторых выражений это не работает:

Атрибуты

Кроме того, вы можете добавить атрибуты во входные параметры или возвращаемое значение, как показано в следующем примере:

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

Лямбда-выражения вызываются через базовый тип делегата. Это отличается от методов и локальных функций. Метод делегата Invoke не проверяет атрибуты в лямбда-выражении. При вызове лямбда-выражения атрибуты не оказывают никакого влияния. Атрибуты лямбда-выражений полезны для анализа кода и могут быть обнаружены с помощью отражения. Одно из последствий этого решения — невозможность применить System.Diagnostics.ConditionalAttribute к лямбда-выражению.

Запись внешних переменных и области видимости переменной в лямбда-выражениях

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

Следующие правила применимы к области действия переменной в лямбда-выражениях.

  • Захваченная переменная не будет уничтожена сборщиком мусора до тех пор, пока делегат, который на нее ссылается, не перейдет в статус подлежащего уничтожению при сборке мусора.
  • Переменные, представленные в лямбда-выражении, невидимы в заключающем методе.
  • Лямбда-выражение не может непосредственно захватывать параметры in, ref или out из заключающего метода.
  • Оператор return в лямбда-выражении не вызывает возврат значения заключающим методом.
  • Лямбда-выражение не может содержать операторы goto, break или continue, если целевой объект этого оператора перехода находится за пределами блока лямбда-выражения. Если целевой объект находится внутри блока, использование оператора перехода за пределами лямбда-выражения также будет ошибкой.

Статическое лямбда-выражение не может сохранять локальные переменные или состояние экземпляров из охватывающих областей, но может ссылаться на статические элементы и определения констант.

При использовании EntityFramework я получаю ошибку " A lambda expression with a statement body cannot be converted to an expression tree " при попытке скомпилировать следующий код:

Является ли objects контекст базы данных Linq-To-SQL? В этом случае вы можете использовать только простые выражения справа от оператора =>. Причина в том, что эти выражения не выполняются, а преобразуются в SQL для выполнения в базе данных. Попробуй это

Вы можете использовать тело оператора в выражении lamba для коллекций IEnumerable . Попробуй это:

Обратите внимание:
при использовании этого метода тщательно продумайте, потому что таким образом у вас будут все результаты запроса в памяти, которые могут иметь нежелательные побочные эффекты для остальной части вашего кода.

@DatVM это зависит от того, что вы собираетесь делать. это не всегда может быть правильным выбором и, конечно, не всегда может быть неправильным выбором.

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

@vbullinger вы правы в некоторой степени, но в более общем смысле (вне контекста linq-to-sql) это более прямой ответ. Это помогло мне с ошибкой

Не зная больше о том, что вы делаете (Linq2Objects, Linq2Entities, Linq2Sql?), Это должно заставить его работать:

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

Используйте эту перегрузку select:

Это работает для меня, но при использовании с Entity Framework это решение будет препятствовать загрузке dbcontext всех строк в память в первую очередь, как это делает AsEnumerable ()?

@par Parliament: для предотвращения загрузки всех строк в память, вы должны использовать Expression .

Возвращаемый объект LINQ to SQL реализовывал IQueryable интерфейс. Поэтому для Select параметра-предиката метода следует указывать только одно лямбда-выражение без тела.

Это связано с тем, что LINQ для кода SQL не выполняется внутри программы, а не на удаленной стороне, такой как сервер SQL или другие. Этот тип выполнения отложенной загрузки был достигнут путем реализации IQueryable, где его ожидаемый делегат обернут в класс типа Expression, как показано ниже.

Дерево выражений не поддерживает лямбда-выражения с телом и его единственная поддержка лямбда-выражений, например var col => col.id );

Читайте также: