diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index 207293b973..269fea5f9a 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -527,7 +527,7 @@ namespace Umbraco.Core.Persistence { using (var cmd = CreateCommand(_sharedConnection, sql, args)) { - var retv=cmd.ExecuteNonQuery(); + var retv=cmd.ExecuteNonQueryWithRetry(); OnExecutedCommand(cmd); return retv; } @@ -559,7 +559,7 @@ namespace Umbraco.Core.Persistence { using (var cmd = CreateCommand(_sharedConnection, sql, args)) { - object val = cmd.ExecuteScalar(); + object val = cmd.ExecuteScalarWithRetry(); OnExecutedCommand(cmd); return (T)Convert.ChangeType(val, typeof(T)); } @@ -1078,7 +1078,7 @@ namespace Umbraco.Core.Persistence IDataReader r; try { - r = cmd.ExecuteReader(); + r = cmd.ExecuteReaderWithRetry(); OnExecutedCommand(cmd); } catch (Exception x) @@ -1254,7 +1254,7 @@ namespace Umbraco.Core.Persistence if (!autoIncrement) { DoPreExecute(cmd); - cmd.ExecuteNonQuery(); + cmd.ExecuteNonQueryWithRetry(); OnExecutedCommand(cmd); return true; } @@ -1265,14 +1265,14 @@ namespace Umbraco.Core.Persistence { case DBType.SqlServerCE: DoPreExecute(cmd); - cmd.ExecuteNonQuery(); + cmd.ExecuteNonQueryWithRetry(); OnExecutedCommand(cmd); id = ExecuteScalar("SELECT @@@IDENTITY AS NewID;"); break; case DBType.SqlServer: cmd.CommandText += ";\nSELECT SCOPE_IDENTITY() AS NewID;"; DoPreExecute(cmd); - id = cmd.ExecuteScalar(); + id = cmd.ExecuteScalarWithRetry(); OnExecutedCommand(cmd); break; case DBType.PostgreSQL: @@ -1280,13 +1280,13 @@ namespace Umbraco.Core.Persistence { cmd.CommandText += string.Format("returning {0} as NewID", EscapeSqlIdentifier(primaryKeyName)); DoPreExecute(cmd); - id = cmd.ExecuteScalar(); + id = cmd.ExecuteScalarWithRetry(); } else { id = -1; DoPreExecute(cmd); - cmd.ExecuteNonQuery(); + cmd.ExecuteNonQueryWithRetry(); } OnExecutedCommand(cmd); break; @@ -1301,14 +1301,14 @@ namespace Umbraco.Core.Persistence param.DbType = DbType.Int64; cmd.Parameters.Add(param); DoPreExecute(cmd); - cmd.ExecuteNonQuery(); + cmd.ExecuteNonQueryWithRetry(); id = param.Value; } else { id = -1; DoPreExecute(cmd); - cmd.ExecuteNonQuery(); + cmd.ExecuteNonQueryWithRetry(); } OnExecutedCommand(cmd); break; @@ -1317,20 +1317,20 @@ namespace Umbraco.Core.Persistence { cmd.CommandText += ";\nSELECT last_insert_rowid();"; DoPreExecute(cmd); - id = cmd.ExecuteScalar(); + id = cmd.ExecuteScalarWithRetry(); } else { id = -1; DoPreExecute(cmd); - cmd.ExecuteNonQuery(); + cmd.ExecuteNonQueryWithRetry(); } OnExecutedCommand(cmd); break; default: cmd.CommandText += ";\nSELECT @@IDENTITY AS NewID;"; DoPreExecute(cmd); - id = cmd.ExecuteScalar(); + id = cmd.ExecuteScalarWithRetry(); OnExecutedCommand(cmd); break; } @@ -1443,7 +1443,7 @@ namespace Umbraco.Core.Persistence DoPreExecute(cmd); // Do it - var retv=cmd.ExecuteNonQuery(); + var retv=cmd.ExecuteNonQueryWithRetry(); OnExecutedCommand(cmd); return retv; } diff --git a/src/Umbraco.Core/Persistence/PetaPocoCommandExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoCommandExtensions.cs index 81ec03e87e..f1b41f2420 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoCommandExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoCommandExtensions.cs @@ -1,4 +1,6 @@ -using System.Data; +using System; +using System.Data; +using System.Data.SqlClient; using Umbraco.Core.Persistence.FaultHandling; namespace Umbraco.Core.Persistence @@ -65,6 +67,129 @@ namespace Umbraco.Core.Persistence } #endregion + #region ExecuteReaderWithRetry method implementations + /// + /// Sends the specified command to the connection and builds a SqlDataReader object containing the results. + /// Uses the default retry policy when executing the command. + /// + /// The command object that is required as per extension method declaration. + /// A System.Data.IDataReader object. + public static IDataReader ExecuteReaderWithRetry(this IDbCommand command) + { + var connectionString = command.Connection.ConnectionString ?? string.Empty; + return ExecuteReaderWithRetry(command, RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(connectionString)); + } + + /// + /// Sends the specified command to the connection and builds a SqlDataReader object containing the results. + /// Uses the specified retry policy when executing the command. + /// + /// The command object that is required as per extension method declaration. + /// The retry policy defining whether to retry a command if a connection fails while executing the command. + /// A System.Data.IDataReader object. + public static IDataReader ExecuteReaderWithRetry(this IDbCommand command, RetryPolicy retryPolicy) + { + var connectionString = command.Connection.ConnectionString ?? string.Empty; + return ExecuteReaderWithRetry(command, retryPolicy, RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(connectionString)); + } + + /// + /// Sends the specified command to the connection and builds a SqlDataReader object containing the results. + /// Uses the specified retry policy when executing the command. Uses a separate specified retry policy when + /// establishing a connection. + /// + /// The command object that is required as per extension method declaration. + /// The command retry policy defining whether to retry a command if it fails while executing. + /// The connection retry policy defining whether to re-establish a connection if it drops while executing the command. + /// A System.Data.IDataReader object. + public static IDataReader ExecuteReaderWithRetry(this IDbCommand command, RetryPolicy cmdRetryPolicy, RetryPolicy conRetryPolicy) + { + //GuardConnectionIsNotNull(command); + + // Check if retry policy was specified, if not, use the default retry policy. + return (cmdRetryPolicy ?? RetryPolicy.NoRetry).ExecuteAction(() => + { + var hasOpenConnection = EnsureValidConnection(command, conRetryPolicy); + + try + { + return command.ExecuteReader(); + } + catch (Exception) + { + if (hasOpenConnection && command.Connection != null && command.Connection.State == ConnectionState.Open) + { + //command.Connection.Close(); + } + + throw; + } + }); + } + + /// + /// Sends the specified command to the connection and builds a SqlDataReader object using one of the + /// CommandBehavior values. Uses the default retry policy when executing the command. + /// + /// The command object that is required as per extension method declaration. + /// One of the System.Data.CommandBehavior values. + /// A System.Data.IDataReader object. + public static IDataReader ExecuteReaderWithRetry(this IDbCommand command, CommandBehavior behavior) + { + var connectionString = command.Connection.ConnectionString ?? string.Empty; + return ExecuteReaderWithRetry(command, behavior, RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(connectionString)); + } + + /// + /// Sends the specified command to the connection and builds a SqlDataReader object using one of the + /// CommandBehavior values. Uses the specified retry policy when executing the command. + /// + /// The command object that is required as per extension method declaration. + /// One of the System.Data.CommandBehavior values. + /// The retry policy defining whether to retry a command if a connection fails while executing the command. + /// A System.Data.SqlClient.SqlDataReader object. + public static IDataReader ExecuteReaderWithRetry(this IDbCommand command, CommandBehavior behavior, RetryPolicy retryPolicy) + { + var connectionString = command.Connection.ConnectionString ?? string.Empty; + return ExecuteReaderWithRetry(command, behavior, retryPolicy, RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(connectionString)); + } + + /// + /// Sends the specified command to the connection and builds a SqlDataReader object using one of the + /// CommandBehavior values. Uses the specified retry policy when executing the command. + /// Uses a separate specified retry policy when establishing a connection. + /// + /// The command object that is required as per extension method declaration. + /// One of the System.Data.CommandBehavior values. + /// The command retry policy defining whether to retry a command if it fails while executing. + /// The connection retry policy defining whether to re-establish a connection if it drops while executing the command. + /// A System.Data.IDataReader object. + public static IDataReader ExecuteReaderWithRetry(this IDbCommand command, CommandBehavior behavior, RetryPolicy cmdRetryPolicy, RetryPolicy conRetryPolicy) + { + //GuardConnectionIsNotNull(command); + + // Check if retry policy was specified, if not, use the default retry policy. + return (cmdRetryPolicy ?? RetryPolicy.NoRetry).ExecuteAction(() => + { + var hasOpenConnection = EnsureValidConnection(command, conRetryPolicy); + + try + { + return command.ExecuteReader(behavior); + } + catch (Exception) + { + if (hasOpenConnection && command.Connection != null && command.Connection.State == ConnectionState.Open) + { + //command.Connection.Close(); + } + + throw; + } + }); + } + #endregion + #region ExecuteScalarWithRetry method implementations /// /// Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.