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

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

Obj[] myArray = objects.Select(o =>
{
    var someLocalVar = o.someVar;

    return new Obj() { 
    Var1 = someLocalVar,
    Var2 = o.var2 };
}).ToArray();

Я не знаю, что означает ошибка и, прежде всего, как ее исправить. Любая помощь?


person pistacchio    schedule 03.03.2011    source источник
comment
попробуйте преобразовать в список, подобный этому. objects.List (). Select (...   -  person nelson eldoro    schedule 10.10.2013


Ответы (10)


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

Arr[] myArray = objects.Select(o => new Obj() { 
    Var1 = o.someVar,
    Var2 = o.var2 
}).ToArray();
person Tim Rogers    schedule 03.03.2011

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

Obj[] myArray = objects.AsEnumerable().Select(o =>
{
    var someLocalVar = o.someVar;

    return new Obj() 
    { 
        Var1 = someLocalVar,
        Var2 = o.var2 
    };
}).ToArray();

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

person Amir Oveisi    schedule 23.02.2013
comment
+1 Мне это нравится! Добавление AsEnumerable() масок решит мою проблему! - person Joel; 09.07.2013
comment
Это реальное решение, принятый ответ сложно применить в некоторых случаях - person Ferran Salguero; 02.08.2013
comment
Нет, это не настоящий ответ. Это заставит ваш запрос выполняться на стороне клиента. Подробнее см. В этом вопросе: stackoverflow.com/questions/33375998/ - person Luke Vo; 27.10.2015
comment
@DatVM это зависит от того, что вы собираетесь делать. это не всегда может быть правильный выбор и, конечно, не всегда может быть неправильный выбор. - person Amir Oveisi; 28.10.2015
comment
Хотя я согласен с вами, OP заявил, что он использует EntityFramework. В большинстве случаев при работе с EF вы хотите, чтобы сторона базы данных выполняла как можно больше работы. Было бы неплохо, если бы вы отметили в своем ответе случай. - person Luke Vo; 28.10.2015
comment
У меня это тоже сработало. Добавление функции AsEnumerable () позволило мне внедрить преобразованное свойство Var в базовый объект без объявления new. - person Arlyn; 10.09.2016
comment
при запросе объектов в БД. Linq по умолчанию реализует Iqueryable. В котором компилятор должен преобразовать linqQuery в DBquery (например, MSSQL Query). Но когда ваш запрос linq содержит какие-либо объекты / методы, которые не могут быть преобразованы в DBQuery. В таких случаях с использованием AsEnumberable, если все данные извлекаются во входящие. память и выполняет методы с помощью компилятора C #. Но даже этот запрос не является отложенным запросом. - person Raghu; 25.10.2016
comment
@Raghu, спасибо за подробности, я отредактировал свой ответ, чтобы он был более ясным. - person Amir Oveisi; 18.11.2016
comment
Поскольку, похоже, никто не указал на это ... это не лямбда-выражение, это Func. - person Corey; 12.07.2019

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

person sepp2k    schedule 03.03.2011
comment
Вы ... немного перефразировали ошибку. Ответ @Tim Rogers был намного лучше - person vbullinger; 27.02.2013
comment
@vbullinger в какой-то степени вы правы, но в более общем смысле (вне контекста linq-to-sql) это более прямой ответ. Мне помогло с ошибкой AutoMapper - person mlhDev; 30.12.2014
comment
vbullinger: Но мне это помогло. - person Paul; 02.01.2019
comment
vbullinger: У меня эта ошибка не связана с Linq-To-SQL. - person Dirk R; 01.02.2021

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

Arr[] myArray = objects.AsEnumerable().Select(o => {
    var someLocalVar = o.someVar;

    return new Obj() { 
        Var1 = someLocalVar,
        Var2 = o.var2 
    }; 
}).ToArray();
person spender    schedule 03.03.2011
comment
Это заставляет запрашиваемый оценить. - person smartcaveman; 03.03.2011
comment
Однако в этом случае это нормально, потому что он все равно вызывает ToArray () сразу после этого. - person smartcaveman; 03.03.2011
comment
не обязательно - кто знает, насколько велико? он может иметь 50 свойств, когда все, что нам нужно, - 2. - person kdawg; 15.04.2011
comment
При использовании этого метода я предпочитаю выбирать поля, которые я буду использовать, в анонимный тип перед вызовом .AsEnumerable() - person Blake Mitchell; 16.12.2014

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

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

Expression<Func<TParam,TResult>>

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

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

Expression<Func<int,int>> function = x => {
    return x * 2;
}

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

Expression<Func<int,int>> function = x => x * 2;
person Azri Jamil    schedule 01.11.2016

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

Obj[] myArray = objects.Select(new Func<Obj,Obj>( o =>
{
    var someLocalVar = o.someVar;

    return new Obj() 
    { 
       Var1 = someLocalVar,
       Var2 = o.var2 
    };
})).ToArray();
person Mohsen    schedule 21.04.2013
comment
Это работает для меня, но при использовании с Entity Framework будет ли это решение препятствовать тому, чтобы dbcontext сначала загружал все строки в память, как это сделал бы AsEnumerable ()? - person parliament; 24.11.2014
comment
@par Parliament: Чтобы не загружать все строки в память, вы должны использовать Expression<Func<Obj,Obj>>. - person Mohsen; 25.11.2014

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

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

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

IQueryable<Obj> result = from o in objects
                         let someLocalVar = o.someVar
                         select new Obj
                         {
                           Var1 = someLocalVar,
                           Var2 = o.var2
                         };

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

Впоследствии вы можете Obj[] myArray = result.ToArray()

person Matthias Burger    schedule 22.07.2020
comment
Я только что посмотрел на это! Желаю, чтобы вы не увидели интригу на моем лице, когда я увидел сообщение 9-летней давности, в котором был добавлен новый ответ. Хахаха Хорошее время. - person TeaBaerd; 22.07.2020
comment
@TeaBaerd, ха-ха, да. : D совпадения бывают забавными ... - person Matthias Burger; 22.07.2020

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

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

Arr[] myArray = objects.Select(o => new Obj()
                {
                   Var1 = o.someVar,
                   Var2 = o.var2
                } ).ToArray();
person smartcaveman    schedule 03.03.2011

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

Obj[] myArray = objects.Select(o =>
{
    var someLocalVar = o.someVar;

    return new Obj() 
    { 
       Var1 = someLocalVar,
       Var2 = o.var2 
    };
}).ToArray();
person Atanas Korchev    schedule 03.03.2011

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

Obj[] myArray = objects
.Select(o => new
{
    SomeLocalVar = o.someVar, // You can even use any LINQ statement here
    Info = o,
}).Select(o => new Obj()
{
    Var1 = o.SomeLocalVar,
    Var2 = o.Info.var2,
    Var3 = o.SomeLocalVar.SubValue1,
    Var4 = o.SomeLocalVar.SubValue2,
}).ToArray();

Изменить: переименовать для соглашения о кодировании C #

person Luke Vo    schedule 27.10.2015