When an IQueryable is created from a linq query, why is it not a "new" variable?
I am using Entity Framework and I get a loop that looks at a set of people and using a foreach loop creates a request for each person's address. As each address request is created, it is added to the node of the tree structure, where it can later be used (to populate child nodes):
IQueryable<Person> pQuery = (IQueryable<Person>)myContext.People; //get a list of people
//go through and get the set of addresses for each person
foreach (var p in pQuery)
{
var addressQuery = from a in myContext.Addresses
from al in a.Address_Links
where al.P_ID == p.P_ID
orderby a.A_POST_CODE
select a;
//add the query to a TreeView node (use the tag to store it)
TreeNode newNode = new TreeNode();
newNode.Tag = addressQuery;
}
Now the problem I find when starting the application is that ALL requests are the last request created i.e. the last iteration of the loop. It's like the addressQuery is created on the first iteration of the loop and then rewritten on each subsequent request. The result of this is that it looks like all address requests in treenodes - these are links to the last request made (?)
Further exploration that I could solve the problem by using a static class to generate the address request and pass that to each TreeNode like this:
public static class Queries
{
public static IQueryable<Address> AddressesForPerson(GenesisEntities myContext, int key)
{
var query = from a in myContext.Addresses
from al in a.Address_Links
where al.P_ID == key
orderby a.A_POST_CODE
select a;
return query;
}
}
The question I have is that I am confused by this behavior. Why do I need a static query class? Can anyone explain to me what is going on?
Confused.Com!
source to share
The reason is that the variable p
(loop variable foreach
) is being committed and the query is being evaluated lazily. As a result, when the request is actually executed, it uses the current value of the variable p
at that time, which is the last value. Read my answer to the question "What is the exact definition of closure?" for more information.
To fix the problem, just try entering a temporary variable:
// `loopVariable` is scoped inside loop body AND the loop declaration.
foreach (var loopVariable in pQuery)
{
var p = loopVariable; // This variable is scoped **inside** loop body.
var addressQuery = from a in myContext.Addresses
from al in a.Address_Links
where al.P_ID == p.P_ID
orderby a.A_POST_CODE
select a;
//add the query to a TreeView node (use the tag to store it)
myTreeView.Tag = addressQuery
}
source to share