使用 foreach 循环加快迭代 List<>涉及每次迭代时的数据库操作



在我正在处理的应用程序中,每个用户都有一个提交。对于每个提交,我需要根据给定的大小(groupSize)创建一组用户(PeerGroup)。例如,对于每个提交,可以创建 3 名学生的PeerGroupsubmissions的大小可以扩展到 1000。

我有以下内容来迭代submissions列表。在每个循环中,我根据组的成员数(PeerGroupMemberships)按升序对用户进行排序。如果用户被分配到更多提交,则它们应位于底部,不应被选中。然后我使用Take来留住这些用户。通过这种方式,我试图保持平衡。

List<Submission> submissions = _context.Submissions.Where(s => s.ReviewRoundId == reviewRoundId).ToList();
foreach (Submission submission in submissions)
{
if (submission.PeerGroup == null)
{
PeerGroup peerGroup = new PeerGroup { SubmissionId = submission.Id};
_context.PeerGroups.Add(peerGroup);
_context.SaveChanges();
IEnumerable<ApplicationUserDto> peers =
_context.ApplicationUsers
.Where(s => s.Submissions.Select(ce => ce.ReviewRoundId).Contains(reviewRoundId))
.Where(s => s.Id != submission.StudentId)   
.OrderBy(m => m.PeerGroupMemberships.Count(pg => pg.PeerGroup.Submission.ReviewRoundId == reviewRoundId))
.Select(m => new ApplicationUserDto
{
FullName = m.FullName,
Id = new Guid(m.Id),
ProfilePhoto = m.ProfilePhoto,
NumberOfPeersToReview = m.PeerGroupMemberships.Count(pg => pg.PeerGroup.Submission.ReviewRoundId == reviewRoun
}).Take(groupSize);

foreach (ApplicationUserDto p in peers)
{
PeerGroupMembership groupMembership = new PeerGroupMembership { UserId = p.Id.ToString(), PeerGroupId = peerGroup.Id  };
_context.PeerGroupMemberships.Add(groupMembership);
_context.SaveChanges();
}
}
}

代码工作正常,但每个循环几乎需要 5 秒,这可能会导致很长的延迟,需要循环 1000 个提交。

我想知道这是否正常,或者代码是否可以以某种方式改进。有什么建议吗?

有几件事:首先,看起来不像是在利用 EF 来映射实体之间的关系。对于常规批量更新方案,通过将 FK 分配给实体并保存所执行的操作通常更有效,但对于插入,当您依赖数据库来分配需要检索以设置 FK 的 PK 时,成本更高。

至少由于要插入对等组和对等组成员资格,因此应映射这两个实体之间的关系,以便可以创建对等组,然后将其关联到新的成员身份,并允许 EF 在执行单个 SaveChanges 调用时计算出 FK。

例如,您的代码可以大大加快速度,如下所示:

var submissions = _context.Submissions
.Where(s => s.ReviewRoundId == reviewRoundId && s.PeerGroup == null)
.Select(s => {Submissionid = s.Id, s.SudentId).ToList();
foreach(var submission in submissions)
{
var peerGroup = new PeerGroup{ SubmissionId = s.SubmissionId };
_context.PeerGroups.Add(peerGroup);
var unassignedUserIds = _context.ApplicationUsers
.Where(u => u.Submissions.Any(s => s.ReviewRoundId == reviewRoundId
&& u.Id != submission.StudentId)
.OrderBy(u => u.PeerGroupMemberships.Count(pg => pg.PeerGroup.Submission.ReviewRoundId == reviewRoundId))
.Select(u => Id)
.Take(groupSize);
foreach(var userId in unassignedUserIds)
{
var groupMembership = new PeerGroupMembership { UserId = userId.ToString(), PeerGroup = peerGroup };
_context.PeerGroupMemberships.Add(groupMemberShip);
}
}
_context.SaveChanges();

关键点:选择提交时,您只需要提交 ID 和学生 ID。我们还可以消除所有已经具有对等组的提交。无需拉其余的。 在拉取对等点时,我们可以使用.Any来查找具有 ReviewRound 的对等点,我们只需要选择用户 ID。 通过 PeerGroup 和 PeerGroupMembership 之间的关系映射,我们可以创建 PeerGroupMembership 实体,并将其 PeerGroup 分配给我们上面创建的新实体。当调用SaveChanges()时,它将插入一组实体,确保首先插入对等组,并在保存成员资格时正确映射FK。 旁注,为什么 PeerGroupMembership 中的 UserId 是一个字符串?这看起来可能是返回应用程序用户的 FK,因此它应该与数据类型匹配。

对于调整性能,目标只是加载您需要的尽可能多的数据,而不是其他任何数据。这适用于读取的列和读取的行 # 。

最新更新