"Вираз лямбда з тілом оператора не може бути перетворений у дерево виразів"


181

Використовуючи 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();

Я не знаю, що означає помилка, і найбільше, як її виправити. Будь-яка допомога?


6
спробуйте перетворити на такий список. object.List (). Виберіть (...
nelson eldoro

Відповіді:


114

Чи objectsконтекст бази даних Linq-To-SQL? У такому випадку ви можете використовувати лише прості вирази праворуч від оператора =>. Причина полягає в тому, що ці вирази не виконуються, а перетворюються в SQL для виконання у базі даних. Спробуйте це

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

102

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

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

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

Зауважте:
ретельно подумайте, використовуючи цей метод, тому що таким чином у вас з’являться всі запити в пам'яті, які можуть мати небажані побічні ефекти на решту коду.


4
+1 Мені це подобається! Якщо додати AsEnumerable()маски, моя проблема піде!
Джоель

5
Це справжнє рішення, прийняту відповідь важко застосувати в деяких випадках
Ферран Сальгеро

15
Ні, це не справжня відповідь. Це зробить ваш запит виконаним на стороні клієнта. Детальніше див. У цьому питанні: stackoverflow.com/questions/33375998/…
Лука Vo

1
@DatVM це залежить від того, що ти будеш робити. це не завжди може бути правильним вибором і, звичайно, не завжди може бути помилковим вибором.
Амір Овеїзі

3
Хоча я згоден з вами, ОП заявила, що він використовує EntityFramework. У більшості випадків, працюючи з EF, ви хочете, щоб на базі даних було виконано якомога більше роботи. Було б добре, якщо ви відзначите випадок у своїй відповіді.
Люк Во

39

Це означає, що ви не можете використовувати лямбда-вирази з "тілом висловлювань" (тобто лямбда-виразами, які використовують фігурні дужки) в місцях, де вираз лямбда потрібно перетворити на дерево виразів (що, наприклад, при використанні linq2sql) .


37
Ви ... трохи перефразувавши помилку. Відповідь @Tim Rogers була набагато краща
vbullinger

2
@vbullinger Ви маєте рацію на ступінь, але в більш загальному розумінні (поза контекстом linq-to-sql) це більш пряма відповідь. Це допомогло мені з помилкою AutoMapper
mlhDev

1
vbullinger: Хоча це і допомогло мені.
Павло

7

Не знаючи більше про те, що ви робите (Linq2Objects, Linq2Entities, Linq2Sql?), Це повинно змусити його працювати:

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

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

11
Це змушує запитуваний оцінювати.
smartcaveman

Однак у цій обставині це нормально, оскільки він як і раніше викликає ToArray ().
smartcaveman

2
не обов’язково - хто знає, наскільки велике "о"? він може мати 50 властивостей, коли все, що ми хочемо, - 2.
kdawg

1
Під час використання цієї методики я люблю вибирати поля, які я буду використовувати в анонімному типі, перш ніж дзвонити.AsEnumerable()
Блейк Мітчелл

4

Використовуйте це перевантаження вибору:

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

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

Це працює для мене, але при використанні з Entity Framework це рішення не дозволить спочатку dbcontext завантажувати всі рядки в пам'ять, як AsEnumerable () буде?
парламент

2
@par Парламент: щоб запобігти завантаженню всіх рядків у пам'ять, ви повинні використовувати Expression<Func<Obj,Obj>>.
Мохсен

4

Об'єкт повернення LINQ до 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;

2

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

Спростіть запит. Наданий вами текст може бути переписаний у такий спосіб, і буде складено:

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

1

Є Arrбазовим типом Obj? Чи існує клас Obj? Ваш код буде працювати лише в тому випадку, якщо Arr є базовим типом Obj. Ви можете спробувати це замість цього:

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

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

1

Для вашого конкретного випадку тіло призначене для створення змінної, і для переключення на 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 #

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.