"Un'espressione lambda con un corpo di istruzione non può essere convertita in un albero di espressioni"


181

Usando EntityFramework , ottengo l'errore " A lambda expression with a statement body cannot be converted to an expression tree" quando provo a compilare il seguente codice:

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

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

Non so che cosa significhi l'errore e soprattutto come correggerlo. Qualsiasi aiuto?


6
prova a convertire in un elenco come questo. objects.List (). Seleziona (...
nelson eldoro,

Risposte:


114

È objectsun contesto di database Linq-to-SQL? In tal caso, è possibile utilizzare solo espressioni semplici a destra dell'operatore =>. Il motivo è che queste espressioni non vengono eseguite, ma vengono convertite in SQL per essere eseguite sul database. Prova questo

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

102

È possibile utilizzare il corpo dell'istruzione nell'espressione lamba per le raccolte IEnumerable . prova questo:

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

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

Avviso:
Pensa attentamente quando usi questo metodo, perché in questo modo avrai tutti i risultati delle query in memoria, che potrebbero avere effetti collaterali indesiderati sul resto del codice.


4
+1 Mi piace! Aggiungendo AsEnumerable()maschere il mio problema scompare!
Joel

5
Questa è la vera soluzione, la risposta accettata è difficile da applicare in alcuni casi
Ferran Salguero

15
No, questa non è la vera risposta. Farebbe eseguire la tua query sul lato client. Fare riferimento a questa domanda per i dettagli: stackoverflow.com/questions/33375998/…
Luke Vo

1
@DatVM dipende da cosa hai intenzione di fare. questa non può essere sempre la scelta giusta e ovviamente non può essere sempre la scelta sbagliata.
Amir Oveisi,

3
Sebbene io sia d'accordo con te, l'OP ha dichiarato che stava usando EntityFramework. Nella maggior parte dei casi, quando si lavora con EF, si desidera che il lato database esegua il maggior lavoro possibile. Sarebbe bello se noti il ​​caso nella tua risposta.
Luke Vo,

39

Significa che non è possibile utilizzare espressioni lambda con un "corpo di istruzione" (ovvero espressioni lambda che usano parentesi graffe) in punti in cui l'espressione lambda deve essere convertita in un albero di espressioni (come ad esempio quando si utilizza linq2sql) .


37
Hai ... leggermente riformulato l'errore. La risposta di
@Tim

2
@vbullinger hai ragione, ma in un senso più generale (al di fuori del contesto di linq-to-sql) questa è una risposta più diretta. Mi ha aiutato con un errore di
AutoMapper

1
vbullinger: Mi ha aiutato, comunque.
Paul

7

Senza sapere di più su ciò che stai facendo (Linq2Objects, Linq2Entities, Linq2Sql?), Questo dovrebbe farlo funzionare:

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

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

11
Ciò forza la query a valutare.
smartcaveman

Tuttavia, in questa circostanza va bene, perché chiama ToArray () subito dopo.
smartcaveman

2
non necessariamente - chissà quanto è grande "o"? potrebbe avere 50 proprietà quando tutto ciò che vogliamo sono 2.
kdawg

1
Quando utilizzo questa tecnica, mi piace selezionare i campi che userò in un tipo anonimo prima di chiamare.AsEnumerable()
Blake Mitchell,

4

Usa questo sovraccarico di select:

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

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

Questo funziona per me, ma se utilizzato con Entity Framework questa soluzione impedirebbe a dbcontext di caricare prima tutte le righe in memoria, come farebbe AsEnumerable ()?
parlamento

2
@par Parliament: per evitare di caricare tutte le righe in memoria, è necessario utilizzare Expression<Func<Obj,Obj>>.
Mohsen

4

L'oggetto di ritorno LINQ to SQL stava implementando l' IQueryableinterfaccia. Così perSelect parametro predicato del metodo è necessario fornire solo una singola espressione lambda senza body.

Questo perché LINQ per il codice SQL non viene eseguito all'interno del programma piuttosto che sul lato remoto come SQL Server o altri. Questo tipo di esecuzione di caricamento lento è stato ottenuto implementando IQueryable in cui il suo delegato si aspetta venga avvolto nella classe di tipo Expression come di seguito.

Expression<Func<TParam,TResult>>

L'albero delle espressioni non supporta l'espressione lambda con body e il suo unico supporto come espressione lambda a riga singola come var id = cols.Select( col => col.id );

Quindi se provi il seguente codice non funzionerà.

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

Di seguito funzionerà come previsto.

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

2

Significa che un'espressione Lambda di tipo TDelegateche contiene un ([parameters]) => { some code };non può essere convertita in un Expression<TDelegate>. È la regola.

Semplifica la tua query. Quello che hai fornito può essere riscritto come segue e compilerà:

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

1

È Arrun tipo di base di Obj? Esiste la classe Obj? Il tuo codice funzionerebbe solo se Arr è un tipo base di Obj. Puoi provare questo invece:

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

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

1

Per il tuo caso specifico, il corpo è per la creazione di una variabile e il passaggio a IEnumerableforzerà tutte le operazioni da elaborare sul lato client, propongo la seguente soluzione.

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

Modifica: rinomina per la convenzione di codifica C #

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.