public void Verify_that_547_insert_conflict_is_parsed_correctly()
        {
            const string message = "The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_LocalTable_ForeignTable\". The conflict occurred in database \"AnAmazingDatabase\", table \"dbo.ForeignTable\", column 'Id'.";

            var violation = SqlForeignKeyViolation.Parse(547, message);

            Assert.AreEqual("LocalTable", violation.LocalTable, "LocalTable incorrect.");
            Assert.AreEqual("dbo.ForeignTable", violation.ForeignTable, "ForeignTable incorrect.");
            Assert.AreEqual("Id", violation.ForeignColumn, "ForeignColumn incorrect.");
        }
        /// <summary>
        /// Tries to execute the guarded code.
        /// </summary>
        /// <returns>The return value of type T, on success.</returns>
        /// <exception cref="ProviderInaccessibleException">On unknown errors and when the circuit continues to trip.</exception>
        /// <exception cref="DuplicateKeyException">When duplicate keys are detected.</exception>
        /// <exception cref="DataUpdatedException">When an optimistic concurrency conflict is detected.</exception>
        /// <exception cref="ErrorHandling.TimeoutException">When an action timed out.</exception>
        /// <exception cref="DeadlockedException">When a deadlock is detected.</exception>
        /// <remarks>Only two exceptions need to be catched: ProviderInaccessibleException and RecoverableException, since
        /// DuplicateKeyException, DataUpdatedException, TimeoutException and DeadlockedException all inherit from
        /// RecoverableException.</remarks>
        public async Task <TReturn> Execute()
        {
            var         coolOffPeriod         = retrySettings.CoolOffPeriod;
            int         tryNumber             = 0;
            Func <bool> retryThresholdReached = () => tryNumber >= retrySettings.RetryCount;
            var         exceptionsCaught      = new List <Exception>();

            while (!retryThresholdReached())
            {
                tryNumber++;

                try
                {
                    return(await awaitableAction());
                }
                catch (InvalidOperationException exception)
                {
                    // InvalidOperationException most likely suggests that we tried to execute
                    // when the curcuit breaker was in an invalid state.
                    exceptionsCaught.Add(exception);
                    if (retryThresholdReached())
                    {
                        throw new ProviderInaccessibleException(exceptionsCaught);
                    }
                }
                catch (SqlException sqlException)
                {
                    if (sqlException.Number == SqlErrorCodes.ForeignKeyConstraint)
                    {
                        // This is an error, which is not recoverable here, so throw a new exception instead of retrying.
                        var violation = SqlForeignKeyViolation.Parse(sqlException.Number, sqlException.Message);
                        throw new ForeignKeyException(violation.LocalTable, violation.ForeignTable, violation.ForeignColumn, sqlException.Message, sqlException);
                    }

                    if (sqlException.Number == SqlErrorCodes.UniqueConstraint || sqlException.Number == SqlErrorCodes.UniqueIndexConstraint)
                    {
                        // This is a duplicate key error, which is not recoverable here, so throw a new exception instead of retrying.
                        var violation = SqlUniqueKeyViolation.Parse(sqlException.Number, sqlException.Message);
                        throw new DuplicateKeyException(violation.TableName, violation.KeyName, violation.DuplicateKeyValue, sqlException.Message, sqlException);
                    }

                    if (sqlException.Number == SqlErrorCodes.Deadlock)
                    {
                        // This is an error, which may be recoverable, so only throw if no more retries are left.
                        if (retryThresholdReached())
                        {
                            throw new DeadlockedException(sqlException);
                        }
                    }

                    if (sqlException.Number == SqlErrorCodes.Timeout)
                    {
                        // This is an error, which may be recoverable, so only throw if no more retries are left.
                        if (retryThresholdReached())
                        {
                            throw new TimeoutException(sqlException);
                        }
                    }

                    if (sqlException.Number == SqlErrorCodes.StringOrBinaryDataTruncation)
                    {
                        throw new TruncatedDataException("Data truncated.", sqlException);
                    }

                    if (retryThresholdReached())
                    {
                        // Something is seriously wrong when we get to here. It is not recoverable.
                        throw new ProviderInaccessibleException(sqlException);
                    }
                }
                catch (TransactionAbortedException exception)
                {
                    exceptionsCaught.Add(exception);
                    // Something is seriously wrong when we get this exception. It is not recoverable.
                    throw new ProviderInaccessibleException(methodDescriptor(), exceptionsCaught);
                }
                catch (Exception exception)
                {
                    exceptionsCaught.Add(exception);
                    // A generic exception may come from the guarded code. But since we have no idea what went
                    // wrong, we must assume that the error is not recoverable.
                    throw new ProviderInaccessibleException(methodDescriptor(), exceptionsCaught);
                }

                await Task.Delay(retrySettings.CoolOffPeriod);
            }

            // We have exceeded the retryCount so report that we cannot access the provider
            throw new ProviderInaccessibleException("Retry count exceeded. " + methodDescriptor(), exceptionsCaught);
        }