"Uma expressão lambda com um corpo de instrução não pode ser convertida em uma árvore de expressão"


181

Ao usar o EntityFramework , recebo o erro " A lambda expression with a statement body cannot be converted to an expression tree" ao tentar compilar o seguinte código:

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

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

Não sei o que significa o erro e, acima de tudo, como corrigi-lo. Qualquer ajuda?


6
tente converter para a lista como esta. ()
— nelson eldoro 10/10

Respostas:


114

É objectsum contexto de banco de dados Linq-To-SQL? Nesse caso, você pode usar apenas expressões simples à direita do operador =>. O motivo é que essas expressões não são executadas, mas são convertidas em SQL para serem executadas no banco de dados. Tente isto

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

102

Você pode usar o corpo da instrução na expressão lamba para coleções IEnumerable . tente este:

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

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

Aviso:
pense com cuidado ao usar esse método, pois dessa forma, você terá todos os resultados da consulta na memória, que podem ter efeitos colaterais indesejados no restante do seu código.


4
+1 Eu gosto disso! Adicionando AsEnumerable()máscaras meu problema vai embora!
— Joel

5
Esta é a solução real, a resposta aceita é difícil de aplicar em alguns casos
— Ferran Salguero

15
Não, essa não é a resposta real. Isso tornaria sua consulta executada no lado do cliente. Consulte esta pergunta para obter detalhes: stackoverflow.com/questions/33375998/…
— Luke Vo

1
@DatVM, depende do que você vai fazer. isso nem sempre pode ser a escolha certa e, claro, nem sempre pode ser a escolha errada.
— Amir Oveisi 28/10/2015

3
Embora eu concorde com você, o OP afirmou que ele estava usando o EntityFramework. Na maioria dos casos, ao trabalhar com o EF, você deseja que o lado do banco de dados faça o máximo de trabalho possível. Seria bom se você notar o caso na sua resposta.
— Luke Vo

39

Isso significa que você não pode usar expressões lambda com um "corpo de instrução" (ou seja, expressões lambda que usam chaves) em locais onde a expressão lambda precisa ser convertida em uma árvore de expressão (que é o caso ao usar linq2sql) .


37
Você ... reformulou ligeiramente o erro. Resposta @ Tim Rogers' era muito melhor
— vbullinger

2
@vbullinger você está certo até certo ponto, mas em um sentido mais geral (fora do contexto do linq-sql), essa é uma resposta mais direta. Isso me ajudou com um erro do AutoMapper
— mlhDev 30/12/2014

1
vbullinger: Mas isso me ajudou.
— Paulo

7

Sem saber mais sobre o que você está fazendo (Linq2Objects, Linq2Entities, Linq2Sql?), Isso deve fazer com que funcione:

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

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

11
Isso força o questionável a avaliar.
— smartcaveman

No entanto, nessa circunstância, tudo bem, porque ele chama ToArray () logo depois de qualquer maneira.
— smartcaveman

2
não necessariamente - quem sabe quão grande é o "o"? ele poderia ter 50 propriedades quando todos nós queremos são 2.
— kdawg

1
Ao usar esta técnica, gosto de selecionar os campos que utilizarei em um tipo anônimo antes de ligar.AsEnumerable()
— Blake Mitchell

4

Use esta sobrecarga de seleção:

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

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

Isso funciona para mim, mas quando usada com o Entity Framework, essa solução impediria que o dbcontext carregasse todas as linhas na memória primeiro, como AsEnumerable () faria?
— parlamento

2
@parl Parliament: Para evitar carregar todas as linhas na memória, você deve usar Expression<Func<Obj,Obj>>.
— Mohsen 25/11

4

O objeto de retorno LINQ to SQL estava implementando a IQueryableinterface. Portanto, para o Selectparâmetro predicado do método, você deve fornecer apenas uma expressão lambda sem corpo.

Isso ocorre porque o código LINQ for SQL não é executado dentro do programa e não no lado remoto, como o SQL Server ou outros. Esse tipo de execução de carregamento lento foi conseguido implementando IQueryable onde seu representante esperado está sendo agrupado na classe Tipo de expressão, como abaixo.

Expression<Func<TParam,TResult>>

A árvore de expressão não suporta expressão lambda com body e seu único suporte a expressão lambda de linha única como var id = cols.Select( col => col.id );

Portanto, se você tentar o seguinte código não funcionará.

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

O seguinte funcionará conforme o esperado.

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

2

Isso significa que uma expressão Lambda do tipo TDelegateque contém a ([parameters]) => { some code };não pode ser convertida em um Expression<TDelegate>. É a regra.

Simplifique sua consulta. O que você forneceu pode ser reescrito da seguinte maneira e será compilado:

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

1

É Arrum tipo de base Obj? A classe Obj existe? Seu código funcionaria apenas se Arr fosse um tipo base de Obj. Você pode tentar isso:

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

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

1

Para o seu caso específico, o corpo é para criar uma variável e, alternando para IEnumerableforçará todas as operações a serem processadas no lado do cliente, proponho a seguinte solução.

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: Renomear para Convenção de Codificação C #

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.