当从linq查询创建IQueryable时,为什么它不是“新”变量?

时间:2009-10-16 09:32:43

标签: linq entity-framework byref

我正在使用实体框架,并且有一个查看一组People的循环,并使用foreach循环创建一个查询每个人的地址。在创建每个地址查询时,它会被添加到树视图的节点中,以后可以使用它(填充子节点):

 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;
 }

现在,我在运行应用程序时发现的问题是所有查询都是最后创建的查询,即循环的最后一次迭代。就像在循环的第一次迭代中创建addressQuery,然后在每个后续查询中覆盖它。结果就是它就像treenodes中的所有地址查询都是对最后一次查询的引用(?)

进一步调查我可以通过使用静态类生成地址查询并将其传递到每个TreeNode来解决问题,如下所示:

 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;
        }

}

我的问题是我对这种行为感到困惑。为什么有一个静态查询类可以帮助我?任何人都可以向我解释发生了什么事吗?

Confused.Com!

1 个答案:

答案 0 :(得分:5)

原因是捕获了p变量(foreach循环变量)并且懒惰地评估了查询。因此,当实际运行查询时,它会使用当时p变量的当前值,这是最后一个值。请阅读my answer to "What is the exact definition of a closure?"了解详情。

要解决此问题,只需尝试引入临时变量:

 // `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
 }