Reputation: 58953
I'm using the EntityFramework, I get the error A lambda expression with a statement body cannot be converted to an expression tree
when trying to compile the following code:
Obj[] myArray = objects.Select(o =>
{
var someLocalVar = o.someVar;
return new Obj() {
Var1 = someLocalVar,
Var2 = o.var2 };
}).ToArray();
I don't know what the error means and most of all how to fix it. Any help?
Upvotes: 262
Views: 193245
Reputation: 4652
I got around this issue by simply defining a function:
public Obj MyFunc(Obj o)
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
}
Obj[] myArray = objects.Select(o => MyFunc(o)).ToArray();
Upvotes: -2
Reputation: 16453
If you came here because this is a top google result for this error but you aren't using a list, you can always do something quick and dirty like this:
my original code:
RuleFor(m => m.DocumentName).Must( ...etc...
I want to drill into m
and I know m
is of type FileUploadDTO
and in the above expression it is trying to return a string so I add the following method that I can set a breakpoint on:
private string GetIt(FileUploadDTO dto)
{
return dto.FileName;
}
Then:
RuleFor(m => GetIt(m)).Must( ...etc...
Upvotes: -2
Reputation: 1746
You can use statement body in lamba expression for IEnumerable collections. try this one:
Obj[] myArray = objects.AsEnumerable().Select(o =>
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
}).ToArray();
Notice:
Think carefully when using this method, because this way, you will have all query results in the application's memory, that may have unwanted side effects on the rest of your code.
Upvotes: 131
Reputation: 455
As stated on other replies, you can only use simple expressions to the right of the =>
operator. I suggest this solution, which consists of just creating a method that does what you want to have inside of the 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 };
}
Upvotes: 1
Reputation: 5956
9 years too late to the party, but a different approach to your problem (that nobody has mentioned?):
The statement-body works fine with Func<>
but won't work with Expression<Func<>>
. IQueryable.Select
wants an Expression<>
, because they can be translated for Entity Framework - Func<>
can not.
So you either use the AsEnumerable
and start working with the data in memory (not recommended, if not really neccessary) or you keep working with the IQueryable<>
which is recommended.
There is something called linq query
which makes some things easier:
IQueryable<Obj> result = from o in objects
let someLocalVar = o.someVar
select new Obj
{
Var1 = someLocalVar,
Var2 = o.var2
};
with let
you can define a variable and use it in the select
(or where
,...) - and you keep working with the IQueryable
until you really need to execute and get the objects.
Afterwards you can Obj[] myArray = result.ToArray()
Upvotes: 8
Reputation: 2404
The LINQ to SQL return object were implementing IQueryable
interface. So for Select
method predicate parameter you should only supply single lambda expression without body.
This is because LINQ for SQL code is not execute inside program rather than on remote side like SQL server or others. This lazy loading execution type were achieve by implementing IQueryable where its expect delegate is being wrapped in Expression type class like below.
Expression<Func<TParam,TResult>>
Expression tree do not support lambda expression with body and its only support single line lambda expression like var id = cols.Select( col => col.id );
So if you try the following code won't works.
Expression<Func<int,int>> function = x => {
return x * 2;
}
The following will works as per expected.
Expression<Func<int,int>> function = x => x * 2;
Upvotes: 7
Reputation: 20778
For your specific case, the body is for creating a variable, and switching to IEnumerable
will force all the operations to be processed on client-side, I propose the following solution.
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();
Edit: Rename for C# Coding Convention
Upvotes: 1
Reputation: 21723
Is objects
a Linq-To-SQL database context? In which case, you can only use simple expressions to the right of the => operator. The reason is, these expressions are not executed, but are converted to SQL to be executed against the database.
Try this
Arr[] myArray = objects.Select(o => new Obj() {
Var1 = o.someVar,
Var2 = o.var2
}).ToArray();
Upvotes: 159
Reputation: 4266
Use this overload of select:
Obj[] myArray = objects.Select(new Func<Obj,Obj>( o =>
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
})).ToArray();
Upvotes: 4
Reputation: 42276
It means that a Lambda expression of type TDelegate
which contains a ([parameters]) => { some code };
cannot be converted to an Expression<TDelegate>
. It's the rule.
Simplify your query. The one you provided can be rewritten as the following and will compile:
Arr[] myArray = objects.Select(o => new Obj()
{
Var1 = o.someVar,
Var2 = o.var2
} ).ToArray();
Upvotes: 2
Reputation: 370425
It means that you can't use lambda expressions with a "statement body" (i.e. lambda expressions which use curly braces) in places where the lambda expression needs to be converted to an expression tree (which is for example the case when using linq2sql).
Upvotes: 44
Reputation: 120518
Without knowing more about what you are doing (Linq2Objects, Linq2Entities, Linq2Sql?), this should make it work:
Arr[] myArray = objects.AsEnumerable().Select(o => {
var someLocalVar = o.someVar;
return new Obj() {
Var1 = someLocalVar,
Var2 = o.var2
};
}).ToArray();
Upvotes: 6
Reputation: 30671
Is Arr
a base type of Obj
? Does the Obj class exist? Your code would work only if Arr is a base type of Obj. You can try this instead:
Obj[] myArray = objects.Select(o =>
{
var someLocalVar = o.someVar;
return new Obj()
{
Var1 = someLocalVar,
Var2 = o.var2
};
}).ToArray();
Upvotes: 1