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!

+2


source to share


1 answer


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
 }

      

+5


source







All Articles