“ การแสดงออกแลมบ์ดาที่มีเนื้อหาข้อความไม่สามารถแปลงเป็นต้นไม้แสดงออกได้”


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
ลองแปลงเป็นรายการแบบนี้ objects.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()หน้ากากของฉันหายไป!
Joel

5
นี่เป็นทางออกที่แท้จริงคำตอบที่ยอมรับนั้นยากที่จะนำไปใช้ในบางกรณี
Ferran Salguero

15
ไม่ใช่นี่ไม่ใช่คำตอบที่แท้จริง มันจะทำให้แบบสอบถามของคุณจะถูกดำเนินการในฝั่งไคลเอ็นต์ อ้างถึงคำถามนี้สำหรับรายละเอียด: stackoverflow.com/questions/33375998/…
Luke Vo

1
@DatVM มันขึ้นอยู่กับสิ่งที่คุณกำลังจะทำ นี่ไม่ใช่ทางเลือกที่ถูกต้องเสมอไปและแน่นอนว่าไม่ใช่ทางเลือกที่ผิดเสมอไป
Amir Oveisi

3
แม้ว่าฉันจะเห็นด้วยกับคุณ OP ระบุว่าเขาใช้ EntityFramework กรณีส่วนใหญ่เมื่อทำงานกับ EF คุณต้องการให้ฐานข้อมูลทำงานให้ได้มากที่สุด มันจะดีถ้าคุณบันทึกกรณีในคำตอบของคุณ
ลุค Vo

39

หมายความว่าคุณไม่สามารถใช้แลมบ์ดานิพจน์ด้วย "ข้อความเนื้อหา" (เช่นนิพจน์แลมบ์ดาที่ใช้เครื่องหมายปีกกา) ในสถานที่ที่นิพจน์แลมบ์ดาจะต้องถูกแปลงเป็นนิพจน์ต้นไม้ (ซึ่งเป็นกรณีตัวอย่างเมื่อใช้ linq2sql) .


37
คุณ ... ข้อผิดพลาด rephrased เล็กน้อย คำตอบของ
@Tim Rogers

2
@vbullinger คุณถูกต้องในระดับหนึ่ง แต่ในความหมายทั่วไป (นอกบริบทของ linq-to-sql) นี่เป็นคำตอบที่ตรงกว่า มันช่วยฉันด้วยข้อผิดพลาด
AutoMapper

1
vbullinger: มันช่วยฉันได้
Paul

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
ไม่จำเป็น - ใครจะรู้ว่า "o" ใหญ่แค่ไหน? มันอาจมี 50 คุณสมบัติเมื่อเราต้องการคือ 2
kdawg

1
เมื่อใช้เทคนิคนี้ฉันชอบเลือกเขตข้อมูลที่ฉันจะใช้เป็นประเภทไม่ระบุชื่อก่อนโทร.AsEnumerable()
Blake Mitchell

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
@parliament: Expression<Func<Obj,Obj>>เพื่อป้องกันการโหลดแถวทั้งหมดในหน่วยความจำที่คุณควรใช้
Mohsen

4

วัตถุส่งคืน LINQ ไปยัง SQL กำลังใช้งานIQueryableอินเตอร์เฟส ดังนั้นสำหรับSelectพารามิเตอร์เพรดิเคตเมธอดคุณควรระบุแลมบ์ดาเดียวที่ไม่มีเนื้อความ

นี่เป็นเพราะ LINQ สำหรับรหัส SQL ไม่ได้ดำเนินการภายในโปรแกรมแทนที่จะอยู่ในระยะไกลเช่นเซิร์ฟเวอร์ SQL หรืออื่น ๆ ประเภทการดำเนินการโหลดที่ขี้เกียจนี้ทำได้โดยการใช้ IQueryable โดยคาดว่าตัวแทนจะถูกห่อในคลาสประเภท Expression เช่นด้านล่าง

Expression<Func<TParam,TResult>>

ต้นไม้ Expression ไม่สนับสนุนการแสดงออกแลมบ์ดากับร่างกายและสนับสนุนการแสดงออกแลมบ์ดาบรรทัดเดียวเท่านั้นเช่น 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 #

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.