“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?

pistacchio
sumber
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();
Tim Rogers
sumber
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.

Amir Oveisi
sumber
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) .

sepp2k
sumber
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();
pemboros
sumber
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();
Mohsen
sumber
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;
Azri Jamil
sumber
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();
smartcaveman
sumber
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();
Atanas Korchev
sumber
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

Luke Vo
sumber