Why is this LINQ query not executed when using foreach?
Asked Answered
L

2

10

When creating new objects in a LINQ statement, for example:

var list = new List<string>() { "a", "b", "c" };
var created = from i in list select new A();

With class A looking like this:

class A
{
    public string Label;
}

And then modifying properties in A with a foreach loop:

foreach (var c in created) {
    c.Label = "Set";
}

Why are the values not set when accessing the objects in the IEnumerable afterwards. E.g. the following assertion fails:

Assert.AreEqual("Set", created.ElementAt(2).Label);

I wonder why this happens. I would expect the foreach-statement to execute the query, and trigger the creation of the objects. MSDN documentation states: "Execution of the query is deferred until the query variable is iterated over in a foreach or For Each loop".

I have reproduced this behavior with .NET 4.5 and Mono 3.2.0. Calling ToList on the IEnumerable before accessing the created object makes this problem go away.

Lussier answered 20/9, 2013 at 12:45 Comment(2)
It's the opposite of "not executed" - it's executed twice - and when it's executed a second time, a new set of As are created.Jany
Because created is a query not a collection. Everytime you access it you execute it again. So if you enumerate the query again after the foreach you will get a fresh new set of IEnumerable<A>. That's the nature of deferred execution. If you would have queried an existing collection of A's you would change existing objects which would be persistent.Mulciber
D
7

It's happening because created is a query, not a result. So, every time you enumerate it, you're evaluating the Select over again from scratch.

If you want this to work, make created an actual list, rather than just an IEnumerable representing a query.

For example, add:

created = created.ToList();

You say:

I would expect the foreach-statement to execute the query, and trigger the creation of the objects

This is exactly what is happening. The problem is the creation of the objects happens every time you iterate over created, not just the first time. Since the ElementAt() method iterates over created, it's just creating new As again.

Dramaturgy answered 20/9, 2013 at 12:49 Comment(0)
V
-3

And many years later

From what I understand, the stay in a cycle, the query to Linq that linked to the foreach. I had the case where the code did not execute, in debug it was paused:

For Each Item In dtDatos.Rows
    Dim Str = String.Join(",", (From Dl In Ndtt Where Dl.Field(Of Integer)("MRP") = Item("MRP") And Dl.Field(Of Integer)("ProductID") =  Item("ProductID") Select Dl.Field(Of Integer )("OrdenPid")).Distinct.ToList)
    Item("Folio") = Str
Next

I solved it by putting the comparison values of the query as variables, not as part of the forach item, and it executed.

Dim A As Integer
Dim B As Integer
For Each Item In dtDatos.Rows
    A = Item("MRP")
    B = Item("ProductID")
    Item("Folio") = String.Join(",", (From Dl In Ndtt Where Dl.Field(Of Integer)("MRP") = A And Dl.Field(Of Integer)("ProductID") = B Select Dl.Field(Of Integer)("OrdenPid")).Distinct.ToList)
Next
Volitive answered 21/6 at 18:19 Comment(2)
This isn't really an answer to the original questionCarboxylase
Also, it's unhelpful to take a snippet from your own black-box code. Answer the original question, not your own question. (But then, the question has already been answered sufficiently.)Nefarious

© 2022 - 2024 — McMap. All rights reserved.