gpt4 book ai didi

c# - 将嵌套的 foreach 编写为 LINQ 以聚合两个单独的值

转载 作者:行者123 更新时间:2023-11-30 22:01:17 25 4
gpt4 key购买 nike

我正在尝试汇总用户在游戏中通过一次挑战可以获得的积分和金币总数。玩家在挑战中可以获得金币和积分的等级如下:

  • 挑战
    • 步骤
      • 积木

一个挑战可以有多个步骤,一个步骤可以有多个步骤 block 。

我将其编写为嵌套的 foreach,但这会导致对数据库进行大量查询,我想知道这是否可以通过在数据库上聚合的 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;
}

CoinsWhenCompletedPointsWhenCompleted 是该特定挑战、步骤或步骤 block 的最高分数。

我试过环顾四周,但找不到要聚合的多个值。

感谢任何帮助,谢谢!

最佳答案

这里的主要问题是你有两个 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);

这意味着无论我们遇到多少步骤或用户挑战,我们只会进行两次查询。

此外,我们可以重构您的 foreach 语句,只需在 Linq 中使用 Sum 即可获得每个子集合的汇总总数。

关于c# - 将嵌套的 foreach 编写为 LINQ 以聚合两个单独的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27835922/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com