“带有语句主体的 lambda 表达式无法转换为表达式树”

问题描述 投票:0回答:12

我正在使用EntityFramework,在尝试编译以下代码时出现错误

A lambda expression with a statement body cannot be converted to an expression tree

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

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

我不知道该错误意味着什么,最重要的是如何修复它。有什么帮助吗?

c# linq entity-framework linq-to-entities
12个回答
157
投票

objects
是 Linq-To-SQL 数据库上下文吗?在这种情况下,您只能在 => 运算符右侧使用简单表达式。原因是,这些表达式不会被执行,而是被转换为 SQL 以针对数据库执行。 试试这个

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

131
投票

您可以在 IEnumerable 集合的 lba 表达式中使用语句主体。 试试这个:

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

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

注意事项:
使用此方法时请仔细考虑,因为这样,您将在应用程序的内存中获得所有查询结果,这可能会对代码的其余部分产生不必要的副作用。


45
投票

这意味着您不能在需要将 lambda 表达式转换为表达式树的地方(例如使用 linq2sql 时的情况)使用带有“语句主体”的 lambda 表达式(即使用大括号的 lambda 表达式) ).


7
投票

如果不了解更多关于您正在做什么的信息(Linq2Objects、Linq2Entities、Linq2Sql?),这应该可以使其工作:

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

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

7
投票

LINQ to SQL 返回对象正在实现

IQueryable
接口。因此,对于
Select
方法谓词参数,您应该只提供没有主体的单个 lambda 表达式。

这是因为 LINQ for SQL 代码不是在程序内部执行,而是在远程端(如 SQL Server 或其他服务器)执行。这种延迟加载执行类型是通过实现 IQueryable 来实现的,其中它的期望委托被包装在如下所示的表达式类型类中。

Expression<Func<TParam,TResult>>

表达式树不支持带主体的 lambda 表达式,仅支持单行 lambda 表达式,如

var id = cols.Select( col => col.id );

因此,如果您尝试以下代码将不起作用。

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

以下将按预期工作。

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

6
投票

晚了 9 年,但解决问题的方法不同(没有人提到过?):

语句主体可以与

Func<>
配合使用,但不能与
Expression<Func<>>
配合使用。
IQueryable.Select
想要一个
Expression<>
,因为它们可以翻译为实体框架 -
Func<>
不能。

因此,您要么使用

AsEnumerable
并开始处理内存中的数据(不推荐,如果不是真的必要),或者继续使用推荐的
IQueryable<>
。 有一种叫做
linq query
的东西可以让一些事情变得更容易:

IQueryable<Obj> result = from o in objects
                         let someLocalVar = o.someVar
                         select new Obj
                         {
                           Var1 = someLocalVar,
                           Var2 = o.var2
                         };

使用

let
,您可以定义一个变量并在
select
(或
where
,...)中使用它 - 并且您可以继续使用
IQueryable
,直到您真正需要执行并获取对象为止。

之后你可以

Obj[] myArray = result.ToArray()


4
投票

使用 select 的重载:

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

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

2
投票

这意味着包含

TDelegate
([parameters]) => { some code };
类型的 Lambda 表达式无法转换为
Expression<TDelegate>
。 这是规矩。

简化您的查询。 您提供的可以重写如下并编译:

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

1
投票

Arr
Obj
的基本类型吗? Obj 类存在吗?仅当 Arr 是 Obj 的基本类型时,您的代码才有效。你可以试试这个:

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

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

1
投票

对于您的具体情况,主体用于创建变量,切换到

IEnumerable
将强制所有操作在客户端处理,我建议以下解决方案。

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

编辑:重命名为 C# 编码约定


1
投票

正如其他回复中所述,您只能使用

=>
运算符右侧的简单表达式。我建议这个解决方案,它只需要创建一个方法来执行您想要在 lambda 内部执行的操作:

public void SomeConfiguration() {
    // ...
    Obj[] myArray = objects.Select(o => Method()).ToArray();
    // ..
}

public Obj Method() {
    var someLocalVar = o.someVar;

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

-2
投票

如果您来到这里是因为这是此错误的最佳谷歌结果,但您没有使用列表,那么您总是可以像这样快速而肮脏地做一些事情:

我的原始代码:

RuleFor(m => m.DocumentName).Must( ...etc...

我想深入研究

m
并且我知道
m
FileUploadDTO
类型,并且在上面的表达式中它试图返回一个字符串,所以我添加了以下可以设置断点的方法:

private string GetIt(FileUploadDTO dto)
{
    return dto.FileName;
}

然后:

RuleFor(m => GetIt(m)).Must( ...etc...
© www.soinside.com 2019 - 2024. All rights reserved.