在使用 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();
我不知道错误是什么意思,最重要的是如何解决它。有什么帮助吗?
objects
是 Linq-To-SQL 数据库上下文吗?在这种情况下,您只能在 => 右侧使用简单的表达式。操作员。原因是,这些表达式没有被执行,而是被转换为 SQL 以针对数据库执行。尝试这个
Arr[] myArray = objects.Select(o => new Obj() {
Var1 = o.someVar,
Var2 = o.var2
}).ToArray();
您可以在 IEnumerable 集合的 Lamba 表达式中使用语句体。试试这个:
Obj[] myArray = objects.AsEnumerable().Select(o =>
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
}).ToArray();
注意:使用此方法时请仔细考虑,因为这样一来,您会将所有查询结果都保存在应用程序的内存中,这可能会对您的其余代码产生不必要的副作用。
AsEnumerable()
掩盖了我的问题!
这意味着您不能在需要将 lambda 表达式转换为表达式树的地方(例如使用 linq2sql 时的情况)使用带有“语句体”的 lambda 表达式(即使用花括号的 lambda 表达式) .
在不知道更多关于你在做什么(Linq2Objects、Linq2Entities、Linq2Sql?)的情况下,这应该使它工作:
Arr[] myArray = objects.AsEnumerable().Select(o => {
var someLocalVar = o.someVar;
return new Obj() {
Var1 = someLocalVar,
Var2 = o.var2
};
}).ToArray();
.AsEnumerable()
之前将我将使用的字段选择为匿名类型
LINQ to SQL 返回对象正在实现 IQueryable
接口。因此,对于 Select
方法谓词参数,您应该只提供没有正文的单个 lambda 表达式。
这是因为 LINQ for SQL 代码不是在程序内部执行,而是在远程端(如 SQL 服务器或其他)执行。这种延迟加载执行类型是通过实现 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;
使用这个重载的选择:
Obj[] myArray = objects.Select(new Func<Obj,Obj>( o =>
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
})).ToArray();
Expression<Func<Obj,Obj>>
。
参加聚会已经晚了 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()
from o in objects.Include(x=>x.Nested) let ...
应该可以工作。
这意味着包含 ([parameters]) => { some code };
的 TDelegate
类型的 Lambda 表达式不能转换为 Expression<TDelegate>
。这是规矩。
简化您的查询。您提供的可以重写为以下内容并将编译:
Arr[] myArray = objects.Select(o => new Obj()
{
Var1 = o.someVar,
Var2 = o.var2
} ).ToArray();
Arr
是 Obj
的基本类型吗? Obj 类是否存在?仅当 Arr 是 Obj 的基本类型时,您的代码才有效。你可以试试这个:
Obj[] myArray = objects.Select(o =>
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
}).ToArray();
对于您的具体情况,主体用于创建变量,切换到 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# 编码约定
如其他回复所述,您只能在 =>
运算符右侧使用简单表达式。我建议这个解决方案,它只包括创建一个方法来执行您想要在 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 };
}