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

Pistacchio
quelle
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();
Tim Rogers
quelle
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.

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

sepp2k
quelle
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();
Spender
quelle
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();
Mohsen
quelle
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;
Azri Jamil
quelle
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();
Smartcaveman
quelle
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();
Atanas Korchev
quelle
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

Luke Vo
quelle