private int?PreExecution(IRelationalConnection connection)
        {
            int?errorNumber    = null;
            var testConnection = (TestSqlServerConnection)connection;

            testConnection.ExecutionCount++;
            if (testConnection.ExecutionFailures.Count > 0)
            {
                var fail = testConnection.ExecutionFailures.Dequeue();
                if (fail.HasValue)
                {
                    if (fail.Value)
                    {
                        testConnection.DbConnection.Close();
                        throw SqlExceptionFactory.CreateSqlException(testConnection.ErrorNumber);
                    }

                    errorNumber = testConnection.ErrorNumber;
                }
            }

            return(errorNumber);
        }
    public override void Commit()
    {
        if (_testConnection.CommitFailures.Count > 0)
        {
            var fail = _testConnection.CommitFailures.Dequeue();
            if (fail.HasValue)
            {
                if (fail.Value)
                {
                    this.GetDbTransaction().Rollback();
                }
                else
                {
                    this.GetDbTransaction().Commit();
                }

                _testConnection.DbConnection.Close();
                throw SqlExceptionFactory.CreateSqlException(_testConnection.ErrorNumber, _testConnection.ConnectionId);
            }
        }

        base.Commit();
    }
    public override async Task CommitAsync(CancellationToken cancellationToken = default)
    {
        if (_testConnection.CommitFailures.Count > 0)
        {
            var fail = _testConnection.CommitFailures.Dequeue();
            if (fail.HasValue)
            {
                if (fail.Value)
                {
                    await this.GetDbTransaction().RollbackAsync(cancellationToken);
                }
                else
                {
                    await this.GetDbTransaction().CommitAsync(cancellationToken);
                }

                await _testConnection.DbConnection.CloseAsync();

                throw SqlExceptionFactory.CreateSqlException(_testConnection.ErrorNumber, _testConnection.ConnectionId);
            }
        }

        await base.CommitAsync(cancellationToken);
    }
        public async Task Retries_SaveChanges_on_execution_failure_with_two_contexts(
            bool realFailure,
            bool openConnection,
            bool async)
        {
            CleanContext();

            using (var context = CreateContext())
            {
                using var auditContext = new AuditContext();

                var connection = (TestSqlServerConnection)context.GetService <ISqlServerConnection>();

                connection.ExecutionFailures.Enqueue(new bool?[] { null, realFailure });

                Assert.Equal(ConnectionState.Closed, context.Database.GetDbConnection().State);

                if (openConnection)
                {
                    if (async)
                    {
                        await context.Database.OpenConnectionAsync();
                    }
                    else
                    {
                        context.Database.OpenConnection();
                    }

                    Assert.Equal(ConnectionState.Open, context.Database.GetDbConnection().State);
                }

                context.Products.Add(new Product());
                context.Products.Add(new Product());

                var throwTransientError = true;

                if (async)
                {
                    await new TestSqlServerRetryingExecutionStrategy(context).ExecuteInTransactionAsync(
                        (MainContext: context, AuditContext: auditContext),
                        async(c, ct) =>
                    {
                        var result = await c.MainContext.SaveChangesAsync(false, ct);

                        c.AuditContext.ChangeTracker.Clear();
                        c.AuditContext.Database.SetDbConnection(c.MainContext.Database.GetDbConnection());

                        var currentTransaction = c.AuditContext.Database.CurrentTransaction;
                        if (throwTransientError)
                        {
                            Assert.Null(currentTransaction);
                        }
                        else
                        {
                            Assert.NotNull(currentTransaction);
                        }

                        await c.AuditContext.Database.UseTransactionAsync(
                            c.MainContext.Database.CurrentTransaction !.GetDbTransaction(), ct);

                        Assert.NotSame(currentTransaction, c.AuditContext.Database.CurrentTransaction);

                        await c.AuditContext.Audits.AddAsync(new Audit(), ct);
                        await c.AuditContext.SaveChangesAsync(ct);

                        if (throwTransientError)
                        {
                            throwTransientError = false;
                            throw SqlExceptionFactory.CreateSqlException(10928);
                        }

                        return(result);
                    },
                        (c, _) =>
                    {
                        Assert.True(false);
                        return(Task.FromResult(false));
                    });

                    context.ChangeTracker.AcceptAllChanges();
                }
                else
                {
                    new TestSqlServerRetryingExecutionStrategy(context).ExecuteInTransaction(
                        (MainContext: context, AuditContext: auditContext),
                        c =>
                    {
                        var result = c.MainContext.SaveChanges(false);

                        c.AuditContext.ChangeTracker.Clear();
                        c.AuditContext.Database.SetDbConnection(c.MainContext.Database.GetDbConnection());

                        var currentTransaction = c.AuditContext.Database.CurrentTransaction;
                        if (throwTransientError)
                        {
                            Assert.Null(currentTransaction);
                        }
                        else
                        {
                            Assert.NotNull(currentTransaction);
                        }

                        c.AuditContext.Database.UseTransaction(c.MainContext.Database.CurrentTransaction !.GetDbTransaction());

                        Assert.NotSame(currentTransaction, c.AuditContext.Database.CurrentTransaction);

                        c.AuditContext.Audits.Add(new Audit());
                        c.AuditContext.SaveChanges();

                        if (throwTransientError)
                        {
                            throwTransientError = false;
                            throw SqlExceptionFactory.CreateSqlException(10928);
                        }

                        return(result);
                    },
                        c =>
                    {
                        Assert.True(false);
                        return(false);
                    });

                    context.ChangeTracker.AcceptAllChanges();
                }

                Assert.Equal(openConnection ? 2 : 3, connection.OpenCount);
                Assert.Equal(6, connection.ExecutionCount);

                Assert.Equal(
                    openConnection
                        ? ConnectionState.Open
                        : ConnectionState.Closed, context.Database.GetDbConnection().State);

                if (openConnection)
                {
                    if (async)
                    {
                        await context.Database.CloseConnectionAsync();
                    }
                    else
                    {
                        context.Database.CloseConnection();
                    }
                }

                Assert.Equal(ConnectionState.Closed, context.Database.GetDbConnection().State);
            }

            using (var context = CreateContext())
            {
                Assert.Equal(2, context.Products.Count());
            }
        }