"Ein Lambda-Ausdruck mit einem Anweisungskörper kann nicht in einen Ausdrucksbaum konvertiert werden."


181

Bei Verwendung von EntityFramework wird A lambda expression with a statement body cannot be converted to an expression treebeim Versuch, den folgenden Code zu kompilieren , die Fehlermeldung " " angezeigt :

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

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

Ich weiß nicht, was der Fehler bedeutet und vor allem, wie ich ihn beheben kann. Irgendeine Hilfe?


6
Versuchen Sie, in eine Liste wie diese zu konvertieren. objects.List (). Wählen Sie (...
nelson eldoro

Antworten:


114

Ist objectsein Linq-To-SQL-Datenbankkontext? In diesem Fall können Sie nur einfache Ausdrücke rechts vom Operator => verwenden. Der Grund dafür ist, dass diese Ausdrücke nicht ausgeführt werden, sondern in SQL konvertiert werden, um für die Datenbank ausgeführt zu werden. Versuche dies

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

102

Sie können den Anweisungshauptteil im Lamba-Ausdruck für IEnumerable- Sammlungen verwenden. Probier diese:

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

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

Hinweis:
Überlegen Sie bei der Verwendung dieser Methode sorgfältig, da auf diese Weise alle Abfrageergebnisse im Speicher gespeichert werden, die möglicherweise unerwünschte Nebenwirkungen auf den Rest Ihres Codes haben.


4
+1 Das gefällt mir! Durch AsEnumerable()das Hinzufügen von Masken verschwindet mein Problem!
Joel

5
Dies ist die wirkliche Lösung, die akzeptierte Antwort ist in einigen Fällen schwierig anzuwenden
Ferran Salguero

15
Nein, das ist nicht die richtige Antwort. Dadurch würde Ihre Abfrage clientseitig ausgeführt. Weitere
Luke Vo

1
@DatVM es hängt davon ab, was Sie tun werden. Dies kann nicht immer die richtige Wahl sein und kann natürlich nicht immer die falsche Wahl sein.
Amir Oveisi

3
Obwohl ich Ihnen zustimme, gab das OP an, dass er EntityFramework verwendet. In den meisten Fällen möchten Sie bei der Arbeit mit EF, dass die Datenbankseite so viel Arbeit wie möglich leistet. Es wäre schön, wenn Sie den Fall in Ihrer Antwort vermerken würden.
Luke Vo

39

Dies bedeutet, dass Sie keine Lambda-Ausdrücke mit einem "Anweisungskörper" (dh Lambda-Ausdrücke, die geschweifte Klammern verwenden) an Stellen verwenden können, an denen der Lambda-Ausdruck in einen Ausdrucksbaum konvertiert werden muss (was beispielsweise bei Verwendung von linq2sql der Fall ist). .


37
Sie ... haben den Fehler leicht umformuliert. @ Tim Rogers 'Antwort war viel besser
vbullinger

2
@vbullinger Sie haben bis zu einem gewissen Grad Recht, aber im allgemeineren Sinne (außerhalb des Kontexts von Linq-to-SQL) ist dies eine direktere Antwort. Es half mir mit einem AutoMapper-Fehler
mlhDev

1
vbullinger: Es hat mir aber geholfen.
Paul

7

Ohne mehr darüber zu wissen, was Sie tun (Linq2Objects, Linq2Entities, Linq2Sql?), Sollte dies funktionieren:

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

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

11
Dies zwingt den Abfragbaren zur Auswertung.
Smartcaveman

Unter diesen Umständen ist es jedoch in Ordnung, da er ToArray () gleich danach aufruft.
Smartcaveman

2
nicht unbedingt - wer weiß, wie groß "o" ist? es könnte 50 Eigenschaften haben, wenn alles was wir wollen sind 2.
kdawg

1
Wenn ich diese Technik verwende, wähle ich gerne die Felder aus, die ich verwenden möchte, bevor ich anrufe.AsEnumerable()
Blake Mitchell

4

Verwenden Sie diese Überladung von select:

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

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

Dies funktioniert bei mir, aber würde diese Lösung bei Verwendung mit Entity Framework verhindern, dass der Datenbankkontext zuerst alle Zeilen in den Speicher lädt, wie dies bei AsEnumerable () der Fall wäre?
Parlament

2
@parliament: Um zu verhindern, dass alle Zeilen in den Speicher geladen werden, sollten Sie verwenden Expression<Func<Obj,Obj>>.
Mohsen

4

Das LINQ to SQL-Rückgabeobjekt implementierte eine IQueryableSchnittstelle. Daher sollten Sie für SelectParameter für Methodenprädikate nur einen einzelnen Lambda-Ausdruck ohne Körper angeben.

Dies liegt daran, dass LINQ für SQL-Code nicht innerhalb eines Programms ausgeführt wird, sondern auf einer Remote-Seite wie SQL Server oder anderen. Dieser verzögerte Ladeausführungstyp wurde durch die Implementierung von IQueryable erreicht, bei dem der erwartete Delegat wie unten in die Ausdruckstypklasse eingeschlossen wird.

Expression<Func<TParam,TResult>>

Der Ausdrucksbaum unterstützt keinen Lambda-Ausdruck mit body und unterstützt nur einen einzeiligen Lambda-Ausdruck wie var id = cols.Select( col => col.id );

Wenn Sie also versuchen, funktioniert der folgende Code nicht.

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

Das Folgende funktioniert wie erwartet.

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

2

Dies bedeutet, dass ein Lambda-Ausdruck vom Typ, TDelegateder a enthält, ([parameters]) => { some code };nicht in a konvertiert werden kann Expression<TDelegate>. Das ist die Regel.

Vereinfachen Sie Ihre Anfrage. Die von Ihnen angegebene kann wie folgt umgeschrieben werden und wird kompiliert:

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

1

Ist Arrein Basistyp von Obj? Existiert die Obj-Klasse? Ihr Code würde nur funktionieren, wenn Arr ein Basistyp von Obj ist. Sie können dies stattdessen versuchen:

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

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

1

Für Ihren speziellen Fall dient der Body zum Erstellen einer Variablen. Wenn Sie zu wechseln, IEnumerablewerden alle Vorgänge auf clientseitiger Seite verarbeitet. Ich schlage die folgende Lösung vor.

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

Bearbeiten: Umbenennen für C # -Codierungskonvention

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.