我正试图解决这个问题,而不仅仅是把它变成一般伏都教。
我执行EF查询并获取一些数据,我.ToList()
就像这样:
IEnumerable<DatabaseMatch<CatName>> nameMatches = nameLogicMatcher.Match(myIQueryableOfCats).ToList();
有些猫在数据库中出现两次,因为它们有多个名称,但每只猫都有一个主要名称。因此,为了对此进行过滤,我将猫的所有ID都列在一个列表中:
List<int> catIds = nameMatches.Select(c => c.Match.CatId).ToList();
然后我遍历所有不同的ID,获取所有匹配的cat名称,并从列表中删除任何非主要名称的内容,如下所示:
foreach (int catId in catIds.Distinct())
{
var allCatNameMatches = nameMatches.Where(c => c.Match.CatId == catId);
var primaryMatch = allCatNameMatches.FirstOrDefault(c => c.Match.NameType == "Primary Name");
nameMatches = nameMatches.Except(allCatNameMatches.Where(c => c != primaryMatch));
}
现在这个代码,当我第一次运行时,只是挂了。我觉得这很奇怪。我穿过它,它似乎工作,但经过10次迭代(它总共限制在100只猫)它开始减速,然后最终它是冰川,然后完全挂起。
我想也许是错误地做了一些密集的数据库工作,但是分析器显示没有执行SQL,除了检索cat名称的初始列表。
我决定将其从IEnumerable
的nameMatches更改为List
,并将相应的.ToList()
放在最后一行。在我这样做之后,它立即完美地工作。
我想问的问题是,为什么?
答案 0 :(得分:3)
如果没有ToList()
,您正在nameMatches
中建立一个IEnumerable
的嵌套链,等待延迟执行。这可能不是那么糟糕,除了你在执行链的每次迭代时也调用FirstOrDefault
。因此,在迭代次数 n 上,您正在执行循环 n-1 次中包含的过滤器操作。如果您有1000只不同的猫,Linq链将被执行 1000 + 99 + ... + 1 次。 (我认为你有 O(n³)的东西!)
道德是,如果你想使用延迟执行,请确保你只执行一次链。
答案 1 :(得分:0)
让我们简化一下你的代码:
foreach (int catId in catIds.Distinct())
{
var allCatNameMatches = nameMatches.Where(c => c.Match.CatId == catId);
var primaryMatch = null;
nameMatches = nameMatches.Except(allCatNameMatches.Where(c => c != primaryMatch));
}
还有一点:
foreach (int catId in catIds.Distinct())
{
nameMatches = nameMatches.Where(c => c.Match.CatId == catId);
var primaryMatch = null;
nameMatches = nameMatches.Except(nameMatches.Where(c => c != primaryMatch));
}
在后者中很明显,由于延迟执行,foreach
身体的每次传递都会延长Where
和Except
的链。然后记住var primaryMatch = allCatNameMatches.FirstOrDefault
。它不是延迟执行的,因此在foreach
的每次迭代中它都应该执行所有链。因此它会挂起。