"Una expresión lambda con un cuerpo de declaración no se puede convertir en un árbol de expresión"

181

Al usar EntityFramework , aparece el error " A lambda expression with a statement body cannot be converted to an expression tree" al intentar compilar el siguiente código:

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

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

No sé qué significa el error y, sobre todo, cómo solucionarlo. ¿Alguna ayuda?

pistacho
fuente
66
intenta convertir a una lista como esta. objetos.
Lista

Respuestas:

114

¿Es objectsun contexto de base de datos Linq-to-SQL? En cuyo caso, solo puede usar expresiones simples a la derecha del operador =>. La razón es que estas expresiones no se ejecutan, sino que se convierten a SQL para ejecutarse en la base de datos. Prueba esto

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

Puede usar el cuerpo de la declaración en la expresión lamba para colecciones IEnumerable . prueba este:

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

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

Aviso:
piense detenidamente al usar este método, porque de esta manera, tendrá todos los resultados de la consulta en la memoria, que pueden tener efectos secundarios no deseados en el resto de su código.

Amir Oveisi
fuente
44
+1 ¡Me gusta esto! Agregar AsEnumerable()máscaras mi problema desaparece!
Joel
55
Esta es la solución real, la respuesta aceptada es difícil de aplicar en algunos casos
Ferran Salguero
15
No, esta no es la respuesta real. Haría que su consulta se ejecute en el lado del cliente. Consulte esta pregunta para obtener más detalles: stackoverflow.com/questions/33375998/…
Luke Vo
1
@DatVM depende de lo que va a hacer. Esto no siempre puede ser una elección correcta y, por supuesto, no siempre puede ser una elección incorrecta.
Amir Oveisi
3
Aunque estoy de acuerdo con usted, el OP declaró que estaba usando EntityFramework. La mayoría de las veces, cuando trabajas con EF, quieres que el lado de la base de datos haga el mayor trabajo posible. Sería bueno si anota el caso en su respuesta.
Luke Vo
39

Significa que no puede usar expresiones lambda con un "cuerpo de enunciado" (es decir, expresiones lambda que usan llaves) en lugares donde la expresión lambda necesita convertirse en un árbol de expresión (que es el caso cuando se usa linq2sql) .

sepp2k
fuente
37
Usted ... reformuló ligeramente el error. La respuesta de @Tim Rogers fue mucho mejor
vbullinger
2
@vbullinger tienes razón hasta cierto punto, pero en un sentido más general (fuera del contexto de linq-to-sql) esta es una respuesta más directa. Me ayudó con un error de
AutoMapper
1
vbullinger: Sin embargo, me ayudó.
Paul
7

Sin saber más sobre lo que está haciendo (Linq2Objects, Linq2Entities, Linq2Sql?), Esto debería hacer que funcione:

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

    return new Obj() { 
        Var1 = someLocalVar,
        Var2 = o.var2 
    }; 
}).ToArray();
gastador
fuente
11
Esto obliga a la consultable a evaluar.
smartcaveman
Sin embargo, en esta circunstancia está bien, porque de todos modos llama a ToArray () inmediatamente después.
smartcaveman
2
no necesariamente, ¿quién sabe qué tan grande es "o"? podría tener 50 propiedades cuando lo único que queremos son 2.
kdawg
1
Cuando uso esta técnica, me gusta seleccionar los campos que usaré en un tipo anónimo antes de llamar.AsEnumerable()
Blake Mitchell
4

Use esta sobrecarga de select:

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

    return new Obj() 
    { 
       Var1 = someLocalVar,
       Var2 = o.var2 
    };
})).ToArray();
Mohsen
fuente
Esto funciona para mí, pero cuando se usa con Entity Framework, ¿esta solución evitaría que dbcontext cargue primero todas las filas en la memoria, como lo haría AsEnumerable ()?
Parlamento el
2
@parliament: para evitar cargar todas las filas en la memoria, debe usarlas Expression<Func<Obj,Obj>>.
Mohsen
4

El objeto de retorno LINQ to SQL estaba implementando la IQueryableinterfaz. Entonces, para el Selectparámetro de predicado de método, solo debe proporcionar una sola expresión lambda sin cuerpo.

Esto se debe a que el código LINQ for SQL no se ejecuta dentro del programa en lugar de en el lado remoto, como el servidor SQL u otros. Este tipo de ejecución de carga diferida se logró mediante la implementación de IQueryable donde su delegado esperado se está envolviendo en la clase de tipo Expresión como se muestra a continuación.

Expression<Func<TParam,TResult>>

El árbol de expresión no admite la expresión lambda con cuerpo y solo admite la expresión lambda de una sola línea como var id = cols.Select( col => col.id );

Entonces, si intentas, el siguiente código no funcionará.

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

Lo siguiente funcionará según lo esperado.

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

Significa que una expresión Lambda de tipo TDelegateque contiene un ([parameters]) => { some code };no se puede convertir en unExpression<TDelegate> . Es la regla

Simplifica tu consulta. El que proporcionó puede reescribirse como sigue y compilará:

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

Es Arrun tipo base de Obj? ¿Existe la clase Obj? Su código funcionaría solo si Arr es un tipo base de Obj. Puedes probar esto en su lugar:

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

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

Para su caso específico, el cuerpo es para crear una variable, y cambiar a IEnumerableforzará a que todas las operaciones se procesen en el lado del cliente, propongo la siguiente solución.

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

Editar: Cambiar el nombre de la Convención de codificación C #

Luke Vo
fuente