C# Linq多个where查询

C# Linq多个where查询,c#,.net,linq,entity-framework-4,C#,.net,Linq,Entity Framework 4,我在构建一个相当大的linq查询时遇到了一个问题。基本上,我需要在循环中执行子查询,以过滤从数据库返回的匹配数。下面的循环中有示例代码: foreach (Guid parent in parentAttributes) { var subQuery = from sc in db.tSearchIndexes join a in db.tAttributes on sc.Attribu

我在构建一个相当大的linq查询时遇到了一个问题。基本上,我需要在循环中执行子查询,以过滤从数据库返回的匹配数。下面的循环中有示例代码:

        foreach (Guid parent in parentAttributes)
        {
            var subQuery = from sc in db.tSearchIndexes
                           join a in db.tAttributes on sc.AttributeGUID equals a.GUID
                           join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID
                           where a.RelatedGUID == parent && userId == pc.CPSGUID                             
                           select sc.CPSGUID;

            query = query.Where(x => subQuery.Contains(x.Id));
         }
当我随后对查询变量调用ToList()时,似乎只执行了一个子查询,剩下一桶我不需要的数据。然而,这种方法是有效的:

       IList<Guid> temp = query.Select(x => x.Id).ToList();

        foreach (Guid parent in parentAttributes)
        {
            var subQuery = from sc in db.tSearchIndexes
                           join a in db.tAttributes on sc.AttributeGUID equals a.GUID
                           join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID
                           where a.RelatedGUID == parent && userId == pc.CPSGUID                             
                           select sc.CPSGUID;

            temp = temp.Intersect(subQuery).ToList();
        }

        query = query.Where(x => temp.Contains(x.Id));
IList temp=query.Select(x=>x.Id.ToList();
foreach(父属性中的Guid父项)
{
var subQuery=来自db.tSearchIndexes中的sc
在sc.AttributeGUID等于a.GUID的db.tatAttributes中加入a
在db.tpeopleIndex中加入pc。GUID等于pc.AttributeGUID
其中a.RelatedGUID==父项&&userId==pc.CPSGUID
选择sc.CPSGUID;
temp=temp.Intersect(子查询).ToList();
}
query=query.Where(x=>temp.Contains(x.Id));

不幸的是,这种方法很糟糕,因为它会导致对远程数据库的多个查询,因此,如果我能让它工作,那么最初的方法只会导致一次命中。有什么想法吗?

我认为您遇到了一个特殊情况,即在用于筛选的lambda表达式中捕获循环变量。也称为访问修改的闭包错误

试试这个:

   foreach (Guid parentLoop in parentAttributes)
    {
        var parent = parentLoop;
        var subQuery = from sc in db.tSearchIndexes
                       join a in db.tAttributes on sc.AttributeGUID equals a.GUID
                       join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID
                       where a.RelatedGUID == parent && userId == pc.CPSGUID                             
                       select sc.CPSGUID;

        query = query.Where(x => subQuery.Contains(x.Id));
     }
问题在于捕获闭包中的
parent
变量(LINQ语法转换为该变量),这会导致所有
子查询
都使用相同的父id运行

编译器生成一个类来保存委托和委托访问的局部变量。编译器对每个循环重新使用该类的相同实例;因此,一旦查询执行,所有
Where
s都将使用相同的
父级
Guid执行,即最后一个执行

在循环作用域内声明
父对象
,会导致编译器从本质上复制要捕获的具有正确值的变量

这可能是有点难以掌握的第一次,所以如果这是第一次击中你;我推荐这两篇文章作为背景和详尽的解释:

  • 埃里克·利珀特:还有
  • 乔恩·斯凯特:
    • 也许是这样

      var subQuery = from sc in db.tSearchIndexes
                     join a in db.tAttributes on sc.AttributeGUID equals a.GUID
                     join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID
                     where parentAttributes.Contains(a.RelatedGUID) && userId == pc.CPSGUID                             
                     select sc.CPSGUID;
      

      +1如果你想更多地理解这个概念,请看这里的Skeet的答案和他的参考文章@谢谢,我添加了一些链接,完全承认我无法像李珀特先生和斯基特先生那样优雅而准确地解释这一点:-)谢谢你,德里斯。工作得很有魅力。您是一位圣人和学者,先生。@dmos我很好奇子查询被具体化了多少次,我认为每次调用contains时,它都会重新具体化列表我更乐意将子查询视为一个列表,而不是和IEnumerableThanks以获得响应k06a,但上面的查询正在尝试(但没有成功)在做一些与您提出的查询稍有不同的事情时。本质上,我是按所有parentAttributes进行过滤的,而您是按任何属性进行过滤的,因此这将返回一组更大的结果。