public async Task <BankUserUpdateModel> Deposit(int id, decimal amount, byte[] accountTimestamp)
        {
            var updateStateModel = new BankUserUpdateModel();

            if (amount <= 0)
            {
                updateStateModel.ErrorList.Add("DepositeAmount", "The amount must be larger than 0.");
                updateStateModel.ErrorList.Add(string.Empty, "Unable to save changes.");
                return(updateStateModel);
            }

            var accountToUpdate = await this.GetAccountById(id);

            if (accountToUpdate == null)
            {
                updateStateModel.ErrorList.Add(string.Empty, "Unable to save changes. The account was deleted by another user.");
                return(updateStateModel);
            }

            // deposit the amount into Account
            accountToUpdate.Balance = accountToUpdate.Balance + amount;

            // set the Timestamp value for the retrieved Account. This Timestamp value will be compared to detech concurrence issue
            _context.Entry(accountToUpdate).Property("Timestamp").OriginalValue = accountTimestamp;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                updateStateModel.ErrorList.Add(string.Empty, "The record you attempted to edit was modified by another user after you got the original value.");

                var exceptionEntry = ex.Entries.Single();
                // Using a NoTracking query means we get the entity but it is not tracked by the context
                // and will not be merged with existing entities in the context.
                var modifiedEntityInDatabase = await _context.BankUsers
                                               .AsNoTracking()
                                               .SingleAsync(d => d.ID == ((BankUser)exceptionEntry.Entity).ID);

                var latestEntityInDb = _context.Entry(modifiedEntityInDatabase);

                // get the latest Balance amount of the Account from Database
                var latestBalance = (Decimal)latestEntityInDb.Property("Balance").CurrentValue;
                if (latestBalance != accountToUpdate.Balance)
                {
                    updateStateModel.ErrorList.Add("User.Balance", $"Current value: {latestBalance:c}");
                }

                // re-update the Timestamp value into User.Timestamp. The deposit process can work for the next Submit
                updateStateModel.EntityTimestamp = (byte[])latestEntityInDb.Property("Timestamp").CurrentValue;
            }

            return(updateStateModel);
        }
Example #2
0
        public async Task <IActionResult> Deposite(AccountViewModel viewModel)
        {
            BankUserUpdateModel result = await _userService.Deposit(viewModel.User.ID, viewModel.DepositeAmount, viewModel.User.Timestamp);

            if (result.ErrorList.Any())
            {
                foreach (var error in result.ErrorList)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }
                viewModel.User.Timestamp = null;
                viewModel.User.Timestamp = result.EntityTimestamp;
                // remove from ModelState to force User.Timestamp to take new value
                ModelState.Remove("User.Timestamp");
            }
            else
            {
                return(RedirectToAction("Index"));
            }

            return(View(viewModel));
        }
Example #3
0
        public async Task <IActionResult> Transfer(TransferViewModel viewModel)
        {
            BankUserUpdateModel result = await _userService.Transfer(
                viewModel.FromUser.ID,
                viewModel.ToUser.ID,
                viewModel.TranferAmount,
                viewModel.FromUser.Timestamp,
                viewModel.ToUser.Timestamp);

            if (result.ErrorList.Any())
            {
                foreach (var error in result.ErrorList)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }
            }
            else
            {
                return(RedirectToAction("Index"));
            }


            return(View(viewModel));
        }
        public async Task <BankUserUpdateModel> Transfer(
            int fromUserId,
            int toUserId,
            decimal amount,
            byte[] fromAccountTimestamp,
            byte[] toAccountTimestamp)
        {
            var updateStateModel = new BankUserUpdateModel();

            if (amount <= 0)
            {
                updateStateModel.ErrorList.Add("TranferAmount", "The amount must be larger than 0.");
                updateStateModel.ErrorList.Add(string.Empty, "Unable to save changes.");
                return(updateStateModel);
            }

            var accountToTransfer = await this.GetAccountById(fromUserId);

            if (accountToTransfer == null)
            {
                updateStateModel.ErrorList.Add(string.Empty, "Unable to save changes. The transfer account was deleted by another user.");
                return(updateStateModel);
            }

            if (accountToTransfer.Balance - amount < 0)
            {
                updateStateModel.ErrorList.Add("TranferAmount", "The withdraw amount must be larger or equals the balance amount.");
                updateStateModel.ErrorList.Add(string.Empty, "Unable to save changes.");
                return(updateStateModel);
            }

            var accountToReceive = await this.GetAccountById(toUserId);

            if (accountToReceive == null)
            {
                updateStateModel.ErrorList.Add(string.Empty, "Unable to save changes. The received account was deleted by another user.");
                return(updateStateModel);
            }

            try
            {
                using (var transaction = _context.Database.BeginTransaction())
                {
                    // create TransferHistory record
                    var transferHistoryRecord = new TransferHistory
                    {
                        FromUserID  = accountToTransfer.ID,
                        ToUserID    = accountToReceive.ID,
                        CreatedDate = DateTime.Now,
                        Amount      = amount
                    };
                    _context.TranferHistories.Add(transferHistoryRecord);

                    // transfer the amount into Account
                    accountToTransfer.Balance = accountToTransfer.Balance - amount;
                    accountToReceive.Balance  = accountToReceive.Balance + amount;

                    // set the Timestamp value for the retrieved Account. This Timestamp value will be compared to detech concurrence issue
                    _context.Entry(accountToTransfer).Property("Timestamp").OriginalValue = fromAccountTimestamp;
                    _context.Entry(accountToReceive).Property("Timestamp").OriginalValue  = toAccountTimestamp;

                    await _context.SaveChangesAsync();

                    transaction.Commit();
                }
            }
            catch (DbUpdateConcurrencyException ex)
            {
                updateStateModel.ErrorList.Add(string.Empty, "The transfer is failed.");

                foreach (var exceptionEntry in ex.Entries)
                {
                    var userId = ((BankUser)exceptionEntry.Entity).ID;
                    // Using a NoTracking query means we get the entity but it is not tracked by the context
                    // and will not be merged with existing entities in the context.
                    var modifiedEntityInDatabase = await _context.BankUsers
                                                   .AsNoTracking()
                                                   .SingleAsync(d => d.ID == userId);

                    var latestEntityInDb = _context.Entry(modifiedEntityInDatabase);

                    // get the latest Balance amount of the Account from Database
                    var latestBalance = (Decimal)latestEntityInDb.Property("Balance").CurrentValue;

                    if (userId == accountToTransfer.ID && latestBalance != accountToTransfer.Balance)
                    {
                        updateStateModel.ErrorList.Add("FromUser.Balance", $"Current value: {latestBalance:c}");
                    }
                    if (userId == accountToReceive.ID && latestBalance != accountToReceive.Balance)
                    {
                        updateStateModel.ErrorList.Add("ToUser.Balance", $"Current value: {latestBalance:c}");
                    }
                }
            }

            return(updateStateModel);
        }