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); }