我试图在一次挑战中聚合用户在游戏中可以获得的积分和硬币的总数。玩家可以获得硬币和挑战点的等级如下:
挑战可以有几个步骤,一个步骤可以有几个步骤。
我已将此作为嵌套的foreach编写,但这会导致对DB的大量查询,我想知道这是否可以通过在DB上聚合的LINQ查询解决,该查询只返回带有两个值的对象,一个用于TotalAchievable积分,一个用于可实现的总金币。
代码如下:
foreach (var challenge in userChallenges)
{
var coins = 0;
var points = 0;
coins += challenge.CoinsWhenCompleted;
points += challenge.PointsWhenCompleted;
var steps = await db.Steps.Where(s => s.ChallengeId == challenge.ChallengeId).ToListAsync().ConfigureAwait(false);
foreach (var step in steps)
{
coins += step.CoinsWhenCompleted;
points += step.PointsWhenCompleted;
var blocks = await db.StepBlocks.Where(b => b.StepId == step.StepId).ToListAsync().ConfigureAwait(false);
foreach (var block in blocks)
{
coins += block.CoinsWhenCompleted;
points += block.PointsWhenCompleted;
}
}
challenge.TotalPossibleCoins = coins;
challenge.TotalPossiblePoints = points;
}
CoinsWhenCompleted
和PointsWhenCompleted
是特定挑战,步骤或步骤块的最高分数。
我已经尝试过四处寻找,但是当有多个值被汇总时无法找到。
感谢任何帮助,谢谢!
答案 0 :(得分:3)
你的主要问题是你有两个select n + 1问题。
您可以将其重写为以下内容:
var challengeIDs = userChallenges.Select(c => c.ChallengeId).Distinct();
var steps = await db.Steps.Where(s => challengeIDs.Contains(s.ChallengeId))
.ToListAsync()
.ConfigureAwait(false);
var stepIDs = steps.Select(s => s.StepId).Distinct();
var blocks = await db.StepBlocks.Where(b => stepIDs.Contains(b.StepId))
.ToListAsync()
.ConfigureAwait(false);
foreach (var challenge in userChallenges)
{
challenge.TotalPossibleCoins = challenge.CoinsWhenCompleted;
challenge.TotalPossiblePoints = challenge.PointsWhenCompleted;
var challengeSteps = steps.Where(s => s.ChallengeId == challenge.ChallengeId).ToList();
var stepBlocks = stepBlocks.Where(b => challengeSteps
.Any(c => c.StepId == b.StepId))
.ToList();
challenge.TotalPossibleCoins += challengeSteps.Sum(c => c.CoinsWhenCompleted);
challenge.TotalPossiblePoints += challengeSteps.Sum(c => c.PointsWhenCompleted);
challenge.TotalPossibleCoins += stepBlocks.Sum(c => c.CoinsWhenCompleted);
challenge.TotalPossiblePoints += stepBlocks.Sum(c => c.PointsWhenCompleted);
}
<强>解释强>
此查询
var steps = await db.Steps.Where(s => s.ChallengeId == challenge.ChallengeId).ToListAsync().ConfigureAwait(false);
和此查询
var blocks = await db.StepBlocks.Where(b => b.StepId == step.StepId).ToListAsync().ConfigureAwait(false);
正在为循环的每次迭代执行 。
为了防止这种情况发生,我们可以简单地执行以上两个查询,但之前 foreach
循环,执行以下操作:
var challengeIDs = userChallenges.Select(c => c.ChallengeId).Distinct();
var steps = await db.Steps.Where(s => challengeIDs.Contains(s.ChallengeId))
.ToListAsync()
.ConfigureAwait(false);
var stepIDs = steps.Select(s => s.StepId).Distinct();
var blocks = await db.StepBlocks.Where(b => stepIDs.Contains(b.StepId))
.ToListAsync()
.ConfigureAwait(false);
这意味着我们只会进行两次查询,无论我们遇到多少步骤或用户挑战。
此外,我们只需在Linq中使用foreach
即可重构您的Sum
语句,以获取每个子集合的汇总总数。