Một biểu thức lambda với phần thân câu lệnh không thể được chuyển đổi thành một cây biểu thức


181

Khi sử dụng EntityFramework , tôi gặp lỗi " A lambda expression with a statement body cannot be converted to an expression tree" khi cố gắng biên dịch mã sau:

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

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

Tôi không biết lỗi có nghĩa là gì và hầu hết tất cả cách khắc phục. Có ai giúp đỡ không?


6
cố gắng chuyển đổi sang danh sách như thế này object.List (). Chọn (...
nelson eldoro

Câu trả lời:


114

objectsbối cảnh cơ sở dữ liệu Linq-To-SQL? Trong trường hợp đó, bạn chỉ có thể sử dụng các biểu thức đơn giản ở bên phải toán tử =>. Lý do là, các biểu thức này không được thực thi, nhưng được chuyển đổi thành SQL để được thực thi đối với cơ sở dữ liệu. Thử cái này

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

102

Bạn có thể sử dụng phần thân câu lệnh trong biểu thức lamba cho các bộ sưu tập IEnumerable . Hãy thử cái này:

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

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

Lưu ý:
Hãy suy nghĩ cẩn thận khi sử dụng phương pháp này, bởi vì theo cách này, bạn sẽ có tất cả kết quả truy vấn trong bộ nhớ, có thể có tác dụng phụ không mong muốn trên phần còn lại của mã của bạn.


4
+1 tôi thích điều này! Thêm mặt AsEnumerable()nạ vấn đề của tôi biến mất!
Joel

5
Đây là giải pháp thực sự, câu trả lời được chấp nhận rất khó áp dụng trong một số trường hợp
Ferran Salguero

15
Không, đây không phải là câu trả lời thực sự. Nó sẽ làm cho truy vấn của bạn được thực hiện ở phía máy khách. Tham khảo câu hỏi này để biết chi tiết: stackoverflow.com/questions/33375998/ từ
Luke Vo

1
@DatVM nó phụ thuộc vào những gì bạn sẽ làm. điều này không thể luôn luôn là lựa chọn đúng và tất nhiên không thể luôn luôn là lựa chọn sai.
Amir Oveisi

3
Mặc dù tôi đồng ý với bạn, OP tuyên bố rằng anh ta đang sử dụng EntityFramework. Hầu hết các trường hợp, khi làm việc với EF, bạn muốn phía cơ sở dữ liệu thực hiện càng nhiều công việc càng tốt. Sẽ thật tốt nếu bạn lưu ý trường hợp trong câu trả lời của bạn.
Luke Võ

39

Điều đó có nghĩa là bạn không thể sử dụng biểu thức lambda với "thân câu lệnh" (nghĩa là biểu thức lambda sử dụng dấu ngoặc nhọn) ở những nơi mà biểu thức lambda cần được chuyển đổi thành cây biểu thức (ví dụ như trường hợp khi sử dụng linq2sql) .


37
Bạn ... hơi nhắc lại lỗi. @ Tim Rogers' câu trả lời tốt hơn nhiều
vbullinger

2
@vbullinger Bạn đúng ở một mức độ, nhưng theo nghĩa chung hơn (bên ngoài bối cảnh của linq-to-sql) thì đây là một câu trả lời trực tiếp hơn. Nó đã giúp tôi với một lỗi
AutoMapper

1
vbullinger: Mặc dù vậy, nó đã giúp tôi.
Paul

7

Không biết thêm về những gì bạn đang làm (Linq2Objects, Linq2Entities, Linq2Sql?), Điều này sẽ làm cho nó hoạt động:

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

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

11
Điều này buộc các truy vấn để đánh giá.
smartcaveman

Tuy nhiên, trong trường hợp này thì không sao, vì dù sao anh ta cũng gọi ToArray ().
smartcaveman

2
không nhất thiết - ai biết "o" lớn như thế nào? nó có thể có 50 thuộc tính khi tất cả những gì chúng ta muốn là 2.
kdawg

1
Khi sử dụng kỹ thuật này, tôi muốn chọn các trường tôi sẽ sử dụng thành một loại ẩn danh trước khi gọi.AsEnumerable()
Blake Mitchell

4

Sử dụng quá tải này của chọn:

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

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

Điều này hoạt động với tôi nhưng khi được sử dụng với Entity Framework, giải pháp này sẽ ngăn dbcontext tải tất cả các hàng vào bộ nhớ trước, như AsEnumerable () sẽ làm gì?
Quốc hội

2
@parferences: Để ngăn tải tất cả các hàng vào bộ nhớ, bạn nên sử dụng Expression<Func<Obj,Obj>>.
Mohsen

4

Đối tượng trả về LINQ to SQL đang thực hiện IQueryablegiao diện. Vì vậy, đối với Selecttham số vị ngữ phương thức, bạn chỉ nên cung cấp biểu thức lambda duy nhất mà không có phần thân.

Điều này là do mã LINQ cho SQL không được thực thi bên trong chương trình thay vì ở phía xa như máy chủ SQL hoặc các máy khác. Kiểu thực thi tải lười biếng này đã đạt được bằng cách triển khai IQueryable trong đó đại biểu kỳ vọng của nó sẽ được gói trong lớp loại Biểu thức như bên dưới.

Expression<Func<TParam,TResult>>

Cây biểu thức không hỗ trợ biểu thức lambda với cơ thể và chỉ hỗ trợ biểu thức lambda dòng đơn như var id = cols.Select( col => col.id );

Vì vậy, nếu bạn thử mã sau đây sẽ không hoạt động.

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

Sau đây sẽ hoạt động như mong đợi.

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

2

Nó có nghĩa là một biểu thức Lambda TDelegatecó chứa ([parameters]) => { some code };không thể được chuyển đổi thành một Expression<TDelegate>. Đó là quy tắc.

Đơn giản hóa truy vấn của bạn. Cái bạn cung cấp có thể được viết lại như sau và sẽ biên dịch:

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

1

Arrmột loại cơ sở của Obj? Lớp Obj có tồn tại không? Mã của bạn sẽ chỉ hoạt động nếu Arr là một loại cơ sở của Obj. Bạn có thể thử điều này thay thế:

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

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

1

Đối với trường hợp cụ thể của bạn, phần thân là để tạo một biến và chuyển sang IEnumerablesẽ buộc tất cả các thao tác được xử lý ở phía máy khách, tôi đề xuất giải pháp sau.

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

Chỉnh sửa: Đổi tên cho Công ước mã hóa C #

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.