[ValidateNewPlayer] // Perform new Player validations (Validators\ValidateNewPlayer.cs) public async Task <IActionResult> Add([FromBody] PlayerAddRequest model) { // at this point model has been validated var newPlayer = _mapper.Map <Player>(model); newPlayer.Active = true; // set initial status of player to Active // ReSharper disable once MethodHasAsyncOverload // AddAsync() overload has special use-case for client-side HiLo generated PKs _db.Players.Add(newPlayer); // Save Unit-of-Work // do not handle exceptions here to let other // services know we failed and they need to retry await _db.SaveChangesAsync(); // TODO: More structured logging with NLog _logger.LogInformation($@">>> PlayerController.Add() success - Id ""{model.TransactionId}"", Name ""{model.PlayerName}""."); var result = _mapper.Map <Player, PlayerModelResponse>(newPlayer); return(Created($"/v1/{ControllerContext.ActionDescriptor.ControllerName}/{model.TransactionId}", result)); }
public async Task <IActionResult> AttemptTransaction([FromRoute] Guid walletGuid, [FromBody] WalletOperationRequest model) { // At this point Model is validated // Get player's Wallet tracked Entity var playersWallet = await _db.Wallet .Where(wallet => wallet.Id == walletGuid) .FirstOrDefaultAsync(); // If we're dealing with negative transaction type if (NegativeTransactionTypes.Contains(model.TransactionType)) { // check if it results in positive Balance in the end if ((playersWallet.Balance - model.Amount) < decimal.Zero) { _logger.LogWarning($@">>> WalletController.AttemptTransaction() - Operation of {nameof(model.TransactionType)} with Amount of {model.Amount} would result in negative balance!."); return(new UnprocessableEntityObjectResult(UnprocessableWalletLogResponseObject)); } // Update Player's Wallet Balance playersWallet.Balance -= model.Amount; } if (PositiveTranscationTypes.Contains(model.TransactionType)) { // Update Player's Wallet Balance playersWallet.Balance += model.Amount; } // perform the operation and log to operations log // runs in an implicit transaction // prepare OkResult var result = new WalletOperationResult { Id = model.TransactionId, Repeated = false, Success = true, WalletState = new WalletModelResponse { Balance = playersWallet.Balance } }; // ReSharper disable once MethodHasAsyncOverload // Async overload serves only purpose for special HiLo generated PKs _db.WalletLogs.Add( new WalletLog { CreatedAt = DateTime.Now, ResultType = ResultType.Created, WalletId = walletGuid, TransactionId = model.TransactionId, Memento = JsonSerializer.Serialize(result, _databaseSerializerOptions) } ); try { await _db.SaveChangesAsync(); } catch (DBConcurrencyException) { // Optimistic concurrency failed as the object // has been changed during this transaction. // Signal this back to the client which is // responsible for retry! _logger.LogDebug($@">>> WalletController.AttemptTransaction() - This transaction failed optimistic concurrency!"); return(new CustomPreconditionFailedResult()); } // TODO: Fill in uri to Get method where the transaction can be found return(Created("", result)); }