Entity Framework获取TeamPoints为0但数据库值非0问题排查
游戏积分更新异常原因排查
问题背景
开发一款游戏时,玩家积分存储到数据库中,两个方法调用同一个UpdateTeam方法:
SendAnswer方法运行正常,能正确更新团队积分ChooseBestAnswer方法调用UpdateTeam时,查询出的oldTeam的TeamPoints为0(但数据库中该值不为0),其余字段正常
相关代码
SendAnswer方法
private async Task SendAnswer(Answer answer) { clicked = true; answerText = answer.AnswerText; team = await gameRepo.GetTeamByNameAndGameSession(Teamname, GameSessionId); if (answer.isCorrect) { team.TeamPoints = team.TeamPoints + answer.Points; } team.Answer = answer; await gameRepo.UpdateTeam(team); if (hubConnection is not null) { await hubConnection.SendAsync("SendTeamAnswer", team, GameSessionId); } }
ChooseBestAnswer方法
private async Task ChooseBestAnswer(Team team) { var answer = currentQuestion.Answers.FirstOrDefault(); team.TeamPoints = team.TeamPoints + answer.Points; await gameRepo.UpdateTeam(team); }
共用的UpdateTeam方法
public async Task UpdateTeam(Team teamToUpdate) { var oldTeam = await _context.Teams.FirstOrDefaultAsync(t => t.TeamName == teamToUpdate.TeamName && t.GameSessionGuid == teamToUpdate.GameSessionGuid); if (teamToUpdate is not null) { oldTeam = teamToUpdate; } _context.SaveChangesAsync(); }
异常原因分析
UpdateTeam方法逻辑错误
方法中先从EF上下文查询出被跟踪的oldTeam实体,但随后直接将oldTeam变量指向传入的teamToUpdate对象——这只是改变了变量的引用,并没有把teamToUpdate的变更同步到EF的上下文跟踪系统中。EF只会对它自己跟踪的实体进行变更检测,所以后续的SaveChangesAsync不会处理teamToUpdate的修改。两个方法传入的team实体状态不同
SendAnswer中的team是通过GetTeamByNameAndGameSession从数据库查询得到的,属于EF上下文跟踪的实体。即使UpdateTeam逻辑错误,EF依然会自动跟踪这个实体的变更,调用SaveChangesAsync时会正常更新数据库,所以该方法表现正常。ChooseBestAnswer中的team是外部传入的,大概率是脱离EF上下文跟踪的对象(比如DTO、前端传入的实体或者之前被上下文释放的实例)。此时UpdateTeam无法将它的变更同步到上下文,而原本查询出的oldTeam被变量覆盖后,EF实际跟踪的还是初始查询的实体(其TeamPoints可能因上下文缓存或未刷新导致为0),最终保存的是这个未更新的旧实体。
未等待异步保存操作
UpdateTeam中调用_context.SaveChangesAsync()时没有使用await,这会导致异步保存操作还未完成就返回方法,可能引发数据未实际保存、上下文状态混乱等问题。
修复方案
修正UpdateTeam方法
正确将传入实体的变更同步到EF跟踪的实体上,并等待异步保存:
public async Task UpdateTeam(Team teamToUpdate) { var oldTeam = await _context.Teams.FirstOrDefaultAsync(t => t.TeamName == teamToUpdate.TeamName && t.GameSessionGuid == teamToUpdate.GameSessionGuid); if (oldTeam != null) { // 将传入实体的属性值复制到上下文跟踪的oldTeam _context.Entry(oldTeam).CurrentValues.SetValues(teamToUpdate); } await _context.SaveChangesAsync(); }
确保ChooseBestAnswer使用跟踪的实体
修改方法,先从数据库查询最新的团队实体再更新,避免使用未跟踪的对象:
private async Task ChooseBestAnswer(string teamName, Guid gameSessionId) { var team = await gameRepo.GetTeamByNameAndGameSession(teamName, gameSessionId); var answer = currentQuestion.Answers.FirstOrDefault(); if (answer != null) { team.TeamPoints += answer.Points; await gameRepo.UpdateTeam(team); } }
内容的提问来源于stack exchange,提问作者Yumaisch
相关产品推荐
相关产品推荐

