Exemple #1
0
        protected void RunInExecutionWrapper(Action run)
        {
            // We wrap this so it will repeat the whole test if there is a timeout or
            // we lose connectivity in the cloud.
            var executionStrategy = new SqlAzureExecutionStrategy();

            SqlAzureDbConfiguration.SuspendExecutionStrategy = true;

            executionStrategy.Execute(run);
        }
Exemple #2
0
        public static IEnumerable <T> QueryResiliently <T>(string sql, dynamic param = null, CommandType?commandType = null)
        {
            IEnumerable <T> result            = null;
            var             executionStrategy = new SqlAzureExecutionStrategy();

            executionStrategy.Execute(() =>
            {
                using (var connection = GetOpenConnection())
                {
                    // QueryAsync is marked async in the source code, but is not in metadata. +https://code.google.com/p/dapper-dot-net/source/browse/Dapper+NET45/SqlMapperAsync.cs
                    result = SqlMapper.Query <T>(connection, sql, param: param, transaction: null, commandTimeout: null, commandType: commandType);
                }
            });
            return(result);
        }
Exemple #3
0
        /// <summary>
        /// Strip the beginning of the exception message which contains the name of the stored procedure and the argument values. We do not disclose the values to the client.
        /// 20150724 AF. We strip the prefix in Runnymede.Website.Utils.CustomExceptionResult
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        //public static Exception StripException(SqlException ex)
        //{
        //    var index = ex.Message.IndexOf("::"); // The magic separator used within stored procedures.
        //    return (index > 0) ? new Exception(ex.Message.Substring(index + 2).Trim(), ex) : ex;
        //}

        public static int ExecuteResiliently(string sql, dynamic param = null, CommandType?commandType = null)
        {
            int rowsAffected      = 0;
            var executionStrategy = new SqlAzureExecutionStrategy();

            executionStrategy.Execute(() =>
            {
                using (var connection = GetOpenConnection())
                {
                    //  Outside-initiated user transactions are not supported. See Limitations with Retrying Execution Strategies (EF6 onwards) +http://msdn.microsoft.com/en-us/data/dn307226
                    // +http://entityframework.codeplex.com/wikipage?title=Connection+Resiliency+Spec
                    // To find a workaround, search for SqlAzureExecutionStrategy in the project code.
                    rowsAffected = SqlMapper.Execute(connection, sql, param: param, transaction: null, commandTimeout: null, commandType: commandType);
                }
            });
            return(rowsAffected);
        }
Exemple #4
0
        public override T BuildAndPersist(IRepository repository = null)
        {
            if (repository == null)
            {
                var executionStrategy = new SqlAzureExecutionStrategy();

                SqlAzureDbConfiguration.SuspendExecutionStrategy = true;

                var entity = executionStrategy.Execute(() => base.BuildAndPersist(null));

                SqlAzureDbConfiguration.SuspendExecutionStrategy = false;

                return(entity);
            }

            return(base.BuildAndPersist(repository));
        }
        public override async Task <T> BuildAndPersistAsync(IAsyncRepository repository = null)
        {
            if (repository == null)
            {
                var executionStrategy = new SqlAzureExecutionStrategy();

                SqlAzureDbConfiguration.SuspendExecutionStrategy = true;

                var entity = await executionStrategy.ExecuteAsync(() => base.BuildAndPersistAsync(null), CancellationToken.None);

                SqlAzureDbConfiguration.SuspendExecutionStrategy = false;

                return(entity);
            }

            return(await base.BuildAndPersistAsync(repository));
        }
Exemple #6
0
        //public static void QueryMultipleResiliently(string sql, dynamic param = null, CommandType? commandType = null, Action<Dapper.SqlMapper.GridReader> action = null)
        //{
        //    try
        //    {
        //        var executionStrategy = new SqlAzureExecutionStrategy();
        //        executionStrategy.Execute(() =>
        //        {
        //            using (var connection = GetOpenConnection())
        //            {
        //                var reader = SqlMapper.QueryMultiple(connection, sql, param: param, transaction: null, commandTimeout: null, commandType: commandType);

        //                if (action != null)
        //                {
        //                    action(reader);
        //                }
        //            }
        //        });
        //    }
        //    catch (SqlException ex)
        //    {
        //        throw StripException(ex);
        //    }
        //}

        public static async Task QueryMultipleResilientlyAsync(string sql, dynamic param = null, CommandType?commandType = null, Action <Dapper.SqlMapper.GridReader> action = null)
        {
            var executionStrategy = new SqlAzureExecutionStrategy();
            await executionStrategy.ExecuteAsync(
                async() =>
            {
                using (var connection = await GetOpenConnectionAsync())
                {
                    var reader = await SqlMapper.QueryMultipleAsync(connection, sql, param: param, transaction: null, commandTimeout: null, commandType: commandType);

                    if (action != null)
                    {
                        action(reader);
                    }
                }
            },
                CancellationToken.None
                );
        }
Exemple #7
0
        public static async Task <IEnumerable <T> > QueryResilientlyAsync <T>(string sql, dynamic param = null, CommandType?commandType = null)
        {
            IEnumerable <T> result            = null;
            var             executionStrategy = new SqlAzureExecutionStrategy();
            await executionStrategy.ExecuteAsync(
                async() =>
            {
                using (var connection = await GetOpenConnectionAsync())
                {
                    // QueryAsync is marked async in source code, but is not in metadata. +https://code.google.com/p/dapper-dot-net/source/browse/Dapper+NET45/SqlMapperAsync.cs
                    result = await SqlMapper.QueryAsync <T>(connection, sql, param: param, transaction: null, commandTimeout: null, commandType: commandType);
                }
            },
                // Apparently, CancellationToken is not used. See +https://github.com/Icehunter/entityframework/blob/master/src/EntityFramework.SqlServer/DefaultSqlExecutionStrategy.cs
                CancellationToken.None
                );

            return(result);
        }
        /// <summary>
        /// Creates the proxy generic.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="target">The target.</param>
        /// <returns>T.</returns>
        public T CreateProxyGeneric <T>(T target) where T : class
        {
            return(Proxy.CreateProxy(target, async invocation =>
            {
                var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
                var targetMethod = target.GetType().GetMember(invocation.Method.Name, flags).First();
                if (IsMarkedNonTransactional(invocation, targetMethod))
                {
                    return await invocation.Proceed();
                }
                else
                {
                    object result = null;

                    if (DynamicExecutionStrategyDbConfiguration.SuspendExecutionStrategy)
                    {
                        using (var trans = _transactionScopeFactory.GetTransactionScope())
                        {
                            result = await invocation.Proceed();
                            trans.Complete();
                        }
                    }
                    else
                    {
                        DynamicExecutionStrategyDbConfiguration.SuspendExecutionStrategy = true;
                        var executionStrategy = new SqlAzureExecutionStrategy(3, TimeSpan.FromMilliseconds(200));

                        await executionStrategy.ExecuteAsync(async() =>
                        {
                            using (var trans = _transactionScopeFactory.GetTransactionScope())
                            {
                                result = await invocation.Proceed();
                                trans.Complete();
                            }
                        }, new System.Threading.CancellationToken());

                        DynamicExecutionStrategyDbConfiguration.SuspendExecutionStrategy = false;
                    }

                    return result;
                }
            }));
        }
Exemple #9
0
        public SqlDbConfiguration(string strategyTypeName)
        {
            IDbExecutionStrategy strategyObj;

            if (strategyTypeName == SqlAzureExecutionStrategy)
            {
                strategyObj = new SqlAzureExecutionStrategy();
            }
            else
            {
                var strategyType = Type.GetType(strategyTypeName);
                strategyObj = (IDbExecutionStrategy)Activator.CreateInstance(strategyType);
            }

            SetExecutionStrategy("System.Data.SqlClient",
                                 () => SuspendExecutionStrategy
                        ? (IDbExecutionStrategy) new DefaultExecutionStrategy()
                        : strategyObj);
        }
Exemple #10
0
        public static async Task <int> ExecuteResilientlyAsync(string sql, dynamic param = null, CommandType?commandType = null)
        {
            int rowsAffected      = 0;
            var executionStrategy = new SqlAzureExecutionStrategy();
            await executionStrategy.ExecuteAsync(
                async() =>
            {
                using (var connection = await GetOpenConnectionAsync())
                {
                    //  Outside-initiated user transactions are not supported. See Limitations with Retrying Execution Strategies (EF6 onwards) +http://msdn.microsoft.com/en-us/data/dn307226
                    // +http://entityframework.codeplex.com/wikipage?title=Connection+Resiliency+Spec
                    // To find a workaround, search for SqlAzureExecutionStrategy in the project code.
                    // Example of using ExecuteAsync(). +https://code.google.com/p/dapper-dot-net/source/browse/DapperTests%20NET45/Tests.cs
                    rowsAffected = await SqlMapper.ExecuteAsync(connection, sql, param: param, transaction: null, commandTimeout: null, commandType: commandType);
                }
            },
                // Apparently, CancellationToken is not used. See +https://github.com/Icehunter/entityframework/blob/master/src/EntityFramework.SqlServer/DefaultSqlExecutionStrategy.cs
                CancellationToken.None
                );

            return(rowsAffected);
        }
Exemple #11
0
        /// <summary>
        ///
        /// Suspend Retry Execution Strategy handling method:
        /// Encapsulate the wrapping db user transaction and final context.SaveChanges() code in this method.
        /// Don't leak the SuspendRetryExecutionStrategy setting change.
        ///
        /// </summary>
        /// <param name="db">The db context that will be for all enclosed within a transaction database update operations</param>
        /// <param name="dbOperationsAction">The delegate with any entity updates operations performed using the db context</param>
        public static void TransactWithRetryStrategy(ApplicationDbContext db, Action dbOperationsAction)
        {
            try {
                ConnRetryConf.SuspendRetryExecutionStrategy = true;

                var executionStrategy = new SqlAzureExecutionStrategy(2, TimeSpan.FromSeconds(10));
                executionStrategy
                .Execute(() => {
                    using (var dbContextTransaction =
                               db.Database.BeginTransaction(IsolationLevel.Serializable))
                    {
                        dbOperationsAction();

                        db.SaveChanges();
                        dbContextTransaction.Commit();
                    }
                });

                // Don't leak suspend setting
            } finally {
                ConnRetryConf.SuspendRetryExecutionStrategy = false;
            }
        }
        private IDbExecutionStrategy LoadExecutionStrategy()
        {
            int?countRanTransactions = (int?)CallContext.LogicalGetData(CallContextKey);
            IDbExecutionStrategy result;

            if (countRanTransactions > 0)
            {
                if (defaultExecutionStrategy == null)
                {
                    defaultExecutionStrategy = new DefaultExecutionStrategy();
                }
                result = defaultExecutionStrategy;
            }
            else
            {
                if (azureExecutionStrategy == null)
                {
                    azureExecutionStrategy = new SqlAzureExecutionStrategy(3, TimeSpan.FromSeconds(30));
                }
                result = azureExecutionStrategy;
            }
            return(result);
        }
Exemple #13
0
        /// <summary>
        /// Saves all changes made in this context to the underlying database
        /// </summary>
        /// <param name="context">The DB context</param>
        /// <returns>The number of objects written to the underlying database</returns>
        private int SaveChanges
        (
            DbContext context
        )
        {
            Validate.IsNotNull(context);

            var success        = false;
            var preEventQueue  = GenerateEventQueue(true);
            var postEventQueue = GenerateEventQueue();
            var aggregates     = GetPendingAggregates();
            var rows           = default(int);

            ProcessEventQueue
            (
                preEventQueue,
                true
            );

            var executionStrategy = new SqlAzureExecutionStrategy();

            AzureEfConfiguration.SuspendExecutionStrategy = true;

            executionStrategy.Execute
            (
                () =>
            {
                using (var transaction = context.Database.BeginTransaction())
                {
                    try
                    {
                        rows = context.SaveChanges();
                        transaction.Commit();

                        success = true;
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();

                        if (ex is DBConcurrencyException ||
                            ex is OptimisticConcurrencyException)
                        {
                            // NOTE:
                            // For concurrency exceptions we want to ensure the
                            // entities are not cached in the context so we can
                            // stop the same error being raised indefinitely.

                            RefreshAll();
                        }

                        throw;
                    }
                }
            }
            );

            AzureEfConfiguration.SuspendExecutionStrategy = false;

            if (success)
            {
                foreach (var aggregate in aggregates)
                {
                    aggregate.UnpublishedEvents.Clear();
                }

                ProcessEventQueue(postEventQueue);
            }

            return(rows);
        }
Exemple #14
0
        private async Task <ApplicationUser> InternalSignup(SignupBindingModel model, ExternalLoginInfo loginInfo, string extId)
        {
            string email    = null;
            string name     = null;
            string password = null;

            // ExternalLoginInfo comes from an external authentication provider, i.e. Facebook or Google.
            if (loginInfo != null)
            {
                email = loginInfo.Email;
                name  = loginInfo.ExternalIdentity.Name;
            }
            // SignupBindingModel comes filled out manually by the user. Override values from the external service with the manually entered values.
            if (model != null)
            {
                email    = model.Email;
                name     = model.Name;
                password = model.Password;
            }

            if ((String.IsNullOrEmpty(password) && loginInfo == null))
            {
                throw new ArgumentNullException();
            }

            var user = new ApplicationUser
            {
                UserName = email,
                Email    = email,
            };

            var displayName = CoalesceDisplayName(name);

            // The internal Connection Resiliency strategy does not work with user-initiated transactions.
            // The workaround is described in "Limitations with Retrying Execution Strategies" +http://msdn.microsoft.com/en-us/data/dn307226.aspx
            AppDbConfiguration.SuspendExecutionStrategy = true; // try-finally is not needed. The value is stored per request.

            var executionStrategy = new SqlAzureExecutionStrategy();

            await executionStrategy.Execute(
                async() =>
            {
                /* We do not use this.userManager within the Connection Resiliency block.
                 * The DbContext should be constructed within the code block to be retried. This ensures that we are starting with a clean state for each retry.
                 */
                using (var dbContext = new ApplicationDbContext())
                    using (var userStore = new CustomUserStore(dbContext))
                        using (var userManager = new ApplicationUserManager(userStore))
                        {
                            // Reuse the original validators.
                            userManager.UserValidator     = this.userManager.UserValidator;
                            userManager.PasswordValidator = this.userManager.PasswordValidator;

                            /* dbo.appGetNewUserId() generates random Ids and tests them against dbo.aspnetUsers until it finds a vacant one.
                             * Yes, fragmentation in the table increases (it would be somewhat anyway due to editing), bad splits on insert ( but not every time after a page has initially been split in halves).
                             * I believe that after the initial page split many next inserts and edits cause a next split not soon.
                             * So the only harm is about 1/4 extra space (not doubled, because we continue filling pages randomly up until the default fillfactor.)
                             * But the space havily depends on what users put in nvarchars anyway, so to worry about the space is pointless.
                             * Clustered index seeks shouldn't be affected. Splits affect only leaf B-tree pages, branch pages have default fillfactor less than 100% always.
                             * The benefit is we get rid of ExtId nchar(12) in many denormalized tables.
                             * Just to remind, the initial idea behind the original ExtId was to prevent a bad guy from guessing sequecial Ids and download all avatars (avatars are accessable directly as Blobs).
                             * So we sent the true Id to the client plus a separate ExtId for avatar URL and we stored it denormalized in many places.
                             * Now ExtId is just a pre-signup cookie value not used anywhere else.
                             */
                            user.Id = await dbContext.Database.SqlQuery <int>("select dbo.appGetNewUserId();").SingleAsync();

                            using (var transaction = dbContext.Database.BeginTransaction())
                            {
                                // First phase. Create an ASP.NET Identity user in dbo.aspnetUsers
                                var identityResult = String.IsNullOrWhiteSpace(password)
                                 ? await userManager.CreateAsync(user)
                                 : await userManager.CreateAsync(user, password);

                                if (identityResult.Succeeded)
                                {
                                    // Second phase. dbo.appUsers. Postpone the creation of the money account until it is really used.
                                    await dbContext.Database.ExecuteSqlCommandAsync(
                                        "insert dbo.appUsers (Id, DisplayName, ExtId) values (@p0, @p1, @p2);",
                                        user.Id, displayName, extId);

                                    /* The UserManager checks the existence of the username in another connection.
                                     * So in the case of an external authorisation (Google, Facebook) AddLoginAsync() does not see the username created by CreateAsync() in our transaction.
                                     * Using IsolationLevel.ReadUncommitted for our transaction would not help since the UserManager reads with ReadCommitted.
                                     * So we are forced to commit prematurely.
                                     */
                                    transaction.Commit();
                                }
                                else
                                {
                                    transaction.Rollback();
                                }

                                if (identityResult.Succeeded && loginInfo != null)
                                {
                                    identityResult = await userManager.AddLoginAsync(user.Id, loginInfo.Login);
                                }

                                if (!identityResult.Succeeded)
                                {
                                    // A hack to report the error to the caller method. See InspectErrorAfterSignup().
                                    user.Id        = 0;
                                    var dummyClaim = new CustomUserClaim
                                    {
                                        ClaimType  = ClaimTypes.UserData,
                                        ClaimValue = identityResult.PlainErrorMessage("Signup failed.")
                                    };
                                    user.Claims.Add(dummyClaim);
                                }
                            }
                        }
            });

            AppDbConfiguration.SuspendExecutionStrategy = false;

            return(user);
        }