views:

116

answers:

1

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 = 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!

+4  A: 

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
 }
Mehrdad Afshari
Wow! That is one hell of a Gotcha! Thanks Mehrdad
Calanus