When IQueryable is created from a linq query why is it not a "new" variable?
I am using the Entity Framework and have got a loop that looks at a set of People and using a foreach loop creates a query for the address of each person. As each address query is created it is added to the node of a treeview where it can be later used (to populate children 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 开发者_Go百科= new TreeNode();
newNode.Tag = addressQuery;
}
Now, the problem that I am finding upon running the app is that ALL the queries are the last query created i.e. the last iteration of the loop. It is like the addressQuery is created on the first iteration of the loop and then overwritten on each subsequent query. The result of this is that it is like all the address queries in the treenodes are references to the last query made(?)
Further investigation that I could solve the problem by using a static class to generate the address query and pass that into each the TreeNode, as follows:
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 baffled by this behaviour. Why does having a static query class help me? Can anyone explain to me what is going on?
Confused.Com!
The reason is that the p
variable (the foreach
loop variable) is captured and the query is evaluated lazily. As a result, when the query is actually run, it uses the current value of p
variable at that time, which is the last value. Read my answer to "What is the exact definition of a closure?" for more info.
To solve the problem, simply try introducing 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
}
精彩评论