我有一个问题是建立一个相当大的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));
不幸的是,这种方法很糟糕,因为它会导致对远程数据库的多次查询,如果我能让它工作的初始方法只会导致单次命中。有什么想法吗?
答案 0 :(得分:8)
我认为你正在尝试捕获用于过滤的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语法转换为),这会导致所有subQuery
es以相同的父ID运行。
编译器生成一个类来保存委托和委托访问的局部变量。编译器为每个循环重用该类的相同实例;因此,一旦查询执行,所有Where
s都使用相同的parent
Guid执行,即最后执行。
在循环范围内声明parent
会导致编译器基本上复制具有正确值的变量。
一开始可能有点难以理解,所以如果这是第一次击中你;我推荐这两篇文章作为背景和详尽的解释:
答案 1 :(得分:0)
也许这样?
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;