I'm working on a game, where players can get points, and players can earn points that are saved in my database. I have one method that looks like this:
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);
}
}
That one works just fine, but then I also have this one in another view:
private async Task ChooseBestAnswer(Team team)
{
var answer = currentQuestion.Answers.FirstOrDefault();
team.TeamPoints = team.TeamPoints answer.Points;
await gameRepo.UpdateTeam(team);
}
Both of them uses this method
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();
}
In the first method everything works as it should but at the second "oldteam" suddenly returns that points = 0 although I can see in the database that it is not 0, how is this possible, I use the same method put it fetches a 0 where there isn't any. All the other variables that are returned from the db to "oldteam" are correct it is just the points that suddenly are zero.
Does anyone know what is going on?
CodePudding user response:
A couple of problems with this code:
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();
}
As mentioned in the comments, the SaveChangesAsync
isn't awaited, but more importantly, this code doesn't update the team in the database. You are loading the existing team, but then simply overwriting the in-memory reference. That doesn't copy values across. Instead:
public async Task UpdateTeam(Team teamToUpdate)
{
if (teamToUpdate == null) throw new NullReferenceException(nameof(teamToUpdate));
var existingTeam = await _context.Teams.SingleAsync(t => t.TeamName == teamToUpdate.TeamName && t.GameSessionGuid == teamToUpdate.GameSessionGuid);
existingTeam.TeamPoints = teamToUpdate.TeamPoints;
// copy any additional fields that are allowed to be updated.
await _context.SaveChangesAsync();
}
Key changes to consider here. Assert the passed in state early and handle if it's invalid. If you expect 1 team to be found, use Single
rather than First
, and if an entry is expected, don't use the OrDefault
variations. Those should be used only if you expect that an item might not be found. Once we have the existing data record, copy the values that can change across and call SaveChanges
, awaiting the async
operation.
This code will throw exceptions if expected state isn't valid, but it will throw meaningful exceptions to be handled at an appropriate level. (Rather than less descriptive exceptions when assumptions aren't met, or failing silently.)