Exemple #1
0
        public override async Task <double> TryGetCreditAsync(
            DbUserAccount userAccount,
            double amountRequested,
            DateTime?timeOfRequestUtc           = null,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (Double.IsNaN(amountRequested) || amountRequested <= 0)
            {
                // You can't request a negative amount and requesting nothing is free
                return(0);
            }

            DateTime timeOfRequestOrNowUtc = timeOfRequestUtc ?? DateTime.Now;

            double creditRetrieved = 0;

            using (var context = new DbUserAccountContext(DbOptions))
            {
                using (var dbContextTransaction = await context.Database.BeginTransactionAsync(cancellationToken))
                {
                    bool rolledBackDueToConcurrencyException = false;
                    do
                    {
                        try
                        {
                            DbUserAccountCreditBalance balance = await
                                                                 context.DbUserAccountCreditBalances.Where(b => b.DbUserAccountId == userAccount.DbUserAccountId)
                                                                 .FirstOrDefaultAsync(cancellationToken: cancellationToken);

                            if (balance == null)
                            {
                                creditRetrieved = Math.Min(amountRequested, userAccount.CreditLimit);
                                double amountRemaining = userAccount.CreditLimit - creditRetrieved;
                                context.DbUserAccountCreditBalances.Add(
                                    new DbUserAccountCreditBalance()
                                {
                                    DbUserAccountId = userAccount.DbUserAccountId,
                                    ConsumedCreditsLastUpdatedUtc = timeOfRequestOrNowUtc,
                                    ConsumedCreditsLastValue      = amountRemaining
                                });
                            }
                            else
                            {
                                double amountAvailable = Math.Max(0, userAccount.CreditLimit -
                                                                  DecayingDouble.Decay(balance.ConsumedCreditsLastValue, userAccount.CreditHalfLife,
                                                                                       balance.ConsumedCreditsLastUpdatedUtc, timeOfRequestOrNowUtc));
                                if (amountAvailable > 0)
                                {
                                    creditRetrieved = Math.Min(amountRequested, amountAvailable);
                                }
                                double amountRemaining = amountAvailable - creditRetrieved;
                                balance.ConsumedCreditsLastValue      = amountRemaining;
                                balance.ConsumedCreditsLastUpdatedUtc = timeOfRequestOrNowUtc;
                            }

                            await context.SaveChangesAsync(cancellationToken);

                            dbContextTransaction.Commit();
                        }
                        catch (DbUpdateConcurrencyException)
                        {
                            dbContextTransaction.Rollback();
                            rolledBackDueToConcurrencyException = true;
                        }
                    } while (rolledBackDueToConcurrencyException);
                }
            }

            return(creditRetrieved);
        }
        public void DecayOverTwoHalfLives()
        {
            double shouldBeVeryCloseTo1 = DecayingDouble.Decay(4, OneDay, FirstDayOfCentury, TwoDaysLater);

            Assert.InRange(shouldBeVeryCloseTo1, .99999, 1.000001);
        }