“Ekspresi lambda dengan tubuh pernyataan tidak dapat dikonversi ke pohon ekspresi”


181

Dalam menggunakan EntityFramework , saya mendapatkan " A lambda expression with a statement body cannot be converted to an expression tree" kesalahan ketika mencoba mengkompilasi kode berikut:

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

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

Saya tidak tahu apa artinya kesalahan dan sebagian besar cara memperbaikinya. Ada bantuan?


6
coba konversi menjadi daftar seperti ini. objects.List (). Pilih (...
nelson eldoro

Jawaban:


114

Apakah objectskonteks basis data Linq-To-SQL? Dalam hal ini, Anda hanya dapat menggunakan ekspresi sederhana di sebelah kanan operator =>. Alasannya adalah, ekspresi ini tidak dieksekusi, tetapi dikonversi ke SQL untuk dieksekusi terhadap database. Coba ini

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

102

Anda dapat menggunakan tubuh pernyataan dalam ekspresi lamba untuk koleksi IEnumerable . coba yang ini:

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

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

Perhatian:
Pikirkan baik-baik ketika menggunakan metode ini, karena dengan cara ini, Anda akan memiliki semua hasil query dalam memori, yang mungkin memiliki efek samping yang tidak diinginkan pada sisa kode Anda.


4
+1 Saya suka ini! Menambahkan AsEnumerable()topeng, masalah saya hilang!
Joel

5
Ini adalah solusi nyata, jawaban yang diterima sulit diterapkan dalam beberapa kasus
Ferran Salguero

15
Tidak, ini bukan jawaban yang sebenarnya. Itu akan membuat kueri Anda dieksekusi di sisi klien. Lihat pertanyaan ini untuk perincian: stackoverflow.com/questions/33375998/…
Luke Vo

1
@DatVM itu tergantung pada apa yang akan Anda lakukan. ini tidak selalu pilihan yang tepat dan tentu saja tidak selalu pilihan yang salah.
Amir Oveisi

3
Meskipun saya setuju dengan Anda, OP menyatakan bahwa ia menggunakan EntityFramework. Sebagian besar kasus, ketika bekerja dengan EF, Anda ingin sisi database melakukan sebanyak mungkin pekerjaan. Akan lebih baik jika Anda mencatat kasus dalam jawaban Anda.
Luke Vo

39

Ini berarti bahwa Anda tidak dapat menggunakan ekspresi lambda dengan "tubuh pernyataan" (yaitu ekspresi lambda yang menggunakan kurung kurawal) di tempat-tempat di mana ekspresi lambda perlu dikonversi ke pohon ekspresi (yang misalnya kasus ketika menggunakan linq2sql) .


37
Anda ... sedikit mengulangi kesalahannya. @Tim Rogers menjawab jauh lebih baik
vbullinger

2
@ vbullinger Anda berhak untuk gelar, tetapi dalam arti yang lebih umum (di luar konteks linq-to-sql) ini adalah jawaban yang lebih langsung. Ini membantu saya dengan kesalahan AutoMapper
mlhDev

1
vbullinger: Tapi itu membantu saya.
Paul

7

Tanpa mengetahui lebih banyak tentang apa yang Anda lakukan (Linq2Objects, Linq2Entities, Linq2Sql?), Ini akan membuatnya bekerja:

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

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

11
Ini memaksa queryable untuk mengevaluasi.
smartcaveman

Namun, dalam keadaan ini tidak apa-apa, karena ia memanggil ToArray () tepat setelah itu.
smartcaveman

2
belum tentu - siapa yang tahu seberapa besar "o"? itu bisa memiliki 50 properti ketika yang kita inginkan adalah 2.
kdawg

1
Saat menggunakan teknik ini, saya suka memilih bidang yang akan saya gunakan menjadi tipe anonim sebelum menelepon.AsEnumerable()
Blake Mitchell

4

Gunakan kelebihan pilih ini:

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

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

Ini berfungsi untuk saya tetapi ketika digunakan dengan Entity Framework akankah solusi ini mencegah dbcontext memuat semua baris terlebih dahulu ke dalam memori, seperti AsEnumerable ()?
parlemen

2
@parlemen: Untuk mencegah pemuatan semua baris ke dalam memori Anda harus menggunakan Expression<Func<Obj,Obj>>.
Mohsen

4

LINQ to SQL return object mengimplementasikan IQueryableantarmuka. Jadi untuk Selectparameter predikat metode, Anda hanya boleh menyediakan ekspresi lambda tunggal tanpa body.

Ini karena LINQ untuk kode SQL tidak dijalankan di dalam program daripada di sisi terpencil seperti SQL server atau lainnya. Tipe eksekusi lazy loading ini dicapai dengan mengimplementasikan IQueryable di mana delegasi ekspektasinya dibungkus dalam kelas tipe Ekspresi seperti di bawah ini.

Expression<Func<TParam,TResult>>

Pohon ekspresi tidak mendukung ekspresi lambda dengan tubuh dan hanya mendukung ekspresi lambda baris tunggal seperti var id = cols.Select( col => col.id );

Jadi, jika Anda mencoba kode berikut tidak akan berfungsi.

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

Berikut ini akan berfungsi seperti yang diharapkan.

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

2

Ini berarti bahwa ekspresi tipe Lambda TDelegateyang berisi a ([parameters]) => { some code };tidak dapat dikonversi ke Expression<TDelegate>. Itu aturannya.

Sederhanakan kueri Anda. Yang Anda berikan dapat ditulis ulang sebagai berikut dan akan dikompilasi:

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

1

Apakah Arrtipe dasar Obj? Apakah kelas Obj ada? Kode Anda hanya akan berfungsi jika Arr adalah tipe dasar dari Obj. Anda dapat mencoba ini sebagai gantinya:

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

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

1

Untuk kasus spesifik Anda, tubuh adalah untuk membuat variabel, dan beralih ke IEnumerableakan memaksa semua operasi untuk diproses di sisi klien, saya mengusulkan solusi berikut.

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();

Sunting: Ganti nama untuk C # Coding Convention

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.