I'm building a Tic-Tac_toe api game. The game work ok, until one of the players press on a square that finish the game(either he wins or fill the board).
When I enabled all CLR exceptions for the last move, this is the exception I saw for line var user = await UserManager.FindByIdAsync(userId);
"Additional information: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe."
Importent to know when a player click a square, it is a 'POST' method. Here's my code:
public class GamesApiController : ApiController
{
ApplicationDbContext context = new ApplicationDbContext();
private ApplicationUserManager _userManager;
public IEnumerable<ApplicationUser> Get()
{
return context.Users;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? System.Web.HttpContext.Current.Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
#region Methods
/// <summary>
/// update the server data by reciving the model and square and returns the new model
/// </summary>
/// <param name="_model"></param>
/// <param name="squareId"></param>
/// <returns></returns>
//square clicked via post
[Route("api/gamesapi/{squareId}")]
public async Task<HttpResponseMessage> Post([FromBody]GameModel model, int squareId)
{
HttpResponseMessage response;
if (model == null)
{
//Utilites.CreateMsgCookie(Response, "Error", "Sorry, an unknown error has occurred");
response = Request.CreateErrorResponse(HttpStatusCode.NotFound, "model wasn't found");
return response;
}
//GameModel model = JsonConvert.DeserializeObject<GameModel>(_model);
Game game = GetGameById(model.Game.GameId);
if (game == null)
{
response = Request.CreateErrorResponse(HttpStatusCode.NotFound, "game wasn't found");
}
else
{
if (game.UserIdTurn == game.User1Id) //pressing user is user1
{
ChangeSquareState(game, squareId, true);
game.UserIdTurn = game.User2Id;
}
else //game.UserIdTurn == game.User2Id - pressing user is user2
{
ChangeSquareState(game, squareId, false);
game.UserIdTurn = game.User1Id;
}
SquareState[] board = new SquareState[] {game.Square1,game.Square2,game.Square3,game.Square4,
game.Square5,game.Square6,game.Square7,game.Square8,game.Square9};
if (didPlayerWin(board))
{
game.WinnerId = model.User.Id;
await UpdateUserGameState(1, game.User1Id);
await UpdateUserGameState(2, game.User2Id);
game.IsGameOver = true;
}
else
{
bool isBoardFull = true;
for (int i = 0; i < board.Length; i++)
{
if (board[i] == SquareState.Blank)
{
isBoardFull = false;
break;
}
}
if (isBoardFull)
{
await UpdateUserGameState(3, game.User1Id);
await UpdateUserGameState(3, game.User2Id);
game.IsGameOver = true;
}
}
context.SaveChanges();
response = Request.CreateResponse(HttpStatusCode.OK, game);
}
return response;
}
/// <summary>
/// When a game is over, recive a gameState and update the user. 1 for a win, 2 for loss, 3 for aa draw
/// </summary>
/// <param name="gameState"></param>
private async Task UpdateUserGameState(int gameState, string userId)
{
var user = await UserManager.FindByIdAsync(userId);
switch (gameState)
{
case 1:
user.GamesWon++;
break;
case 2:
user.GamesLost++;
break;
case 3:
user.GamesDraw++;
break;
default:
break;
}
UserManager.UpdateAsync(user);
}
[HttpGet]
[Route("api/gamesapi/{gameId}")]
/// <summary>
/// method to bring the latest game's state from the context and send it back in a GameModel
/// </summary>
/// <param name="_model"></param>
/// <returns></returns>
public HttpResponseMessage Get(int gameId)
{
HttpResponseMessage response;
Game game = GetGameById(gameId);
if (game == null)
{
response = Request.CreateErrorResponse(HttpStatusCode.NotFound, "game wasn't found");
}
else
{
response = Request.CreateResponse(HttpStatusCode.OK, game);
}
return response;
}
/// <summary>
/// method that check if the board have a line(3 squars in a row)
/// of the same element of user1 or user2 , defult - returns fault
/// </summary>
/// <param name="board"></param>
/// <returns></returns>
private bool didPlayerWin(SquareState[] board)
{
}
/// <summary>
/// change the SquareState of a specific square of the sent game according to the pressing user
/// </summary>
/// <param name="game"></param>
/// <param name="SquareId"></param>
/// <param name="_isUser1"></param>
private void ChangeSquareState(Game game, int SquareId, bool _isUser1)
{
}
/// <summary>
/// get game from context by gameId , Defult result - null
/// </summary>
/// <param name="gameId"></param>
/// <returns></returns>
private Game GetGameById(int gameId)
{
}
#endregion
}
I think you missed to wait for UpdateUserAsync()
in method UpdateUserGameState()
:
private async Task UpdateUserGameState(int gameState, string userId)
{
var user = await UserManager.FindByIdAsync(userId);
// ...
// add missing await
await UserManager.UpdateAsync(user);
}