Linq多个where查询

本文关键字:查询 where 多个 Linq | 更新日期: 2023-09-27 18:27:27

我在构建一个相当庞大的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));

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

Linq多个where查询

我认为您遇到了一个特殊情况,即在用于筛选的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语法转换为该变量),这导致所有subQueryes都使用相同的父id运行。

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

在循环范围内声明parent会导致编译器实质上生成一个要捕获的具有正确值的变量副本。

这一点一开始可能有点难以把握,所以如果这是它第一次袭击你;我推荐这两篇文章作为背景和全面的解释:

  • Eric Lippert:闭环变量被认为是有害的,第二部分
  • Jon Skeet:结束语

也许是这样?

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;