Last active
October 29, 2015 15:59
-
-
Save imranbaloch/27c30178cf4daed1b0b5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//await connection.OpenWithRetryAsync(retryPolicy).ConfigureAwait(false); | |
//var reader = await command.ExecuteReaderWithRetryAsync(retryPolicy).ConfigureAwait(false); | |
public static Task OpenWithRetryAsync(this SqlConnection connection, | |
RetryPolicy retryPolicy) | |
{ | |
return retryPolicy.ExecuteAsync(connection.OpenAsync); | |
} | |
public static class SqlCommandExtensions | |
{ | |
public static async Task<SqlDataReader> ExecuteReaderWithRetryAsync(this SqlCommand command, | |
RetryPolicy retryPolicy) | |
{ | |
return (SqlDataReader) await command.ExecuteWithRetryAsync(retryPolicy, ExecutionType.Reader).ConfigureAwait(false); | |
} | |
public static async Task<int> ExecuteNonQueryWithRetryAsync(this SqlCommand command, | |
RetryPolicy retryPolicy) | |
{ | |
return (int) await command.ExecuteWithRetryAsync(retryPolicy, ExecutionType.NonQuery).ConfigureAwait(false); | |
} | |
public static async Task<object> ExecuteScalarWithRetryAsync(this SqlCommand command, | |
RetryPolicy retryPolicy) | |
{ | |
return await command.ExecuteWithRetryAsync(retryPolicy, ExecutionType.Scaler).ConfigureAwait(false); | |
} | |
public static async Task<object> ExecuteWithRetryAsync(this SqlCommand command, | |
RetryPolicy retryPolicy, | |
ExecutionType executionType) | |
{ | |
return await retryPolicy.ExecuteAsync(async () => | |
{ | |
var hasOpenConnection = await EnsureValidConnectionAsync(command, retryPolicy).ConfigureAwait(false); | |
try | |
{ | |
switch (executionType) | |
{ | |
case ExecutionType.Reader: | |
return await command.ExecuteReaderAsync().ConfigureAwait(false); | |
case ExecutionType.NonQuery: | |
return await command.ExecuteNonQueryAsync().ConfigureAwait(false); | |
default: | |
return await command.ExecuteScalarAsync().ConfigureAwait(false); | |
} | |
} | |
catch (Exception exception) | |
{ | |
if (hasOpenConnection && command.Connection != null && command.Connection.State == ConnectionState.Open)// if we explcitly opened then we have to close it | |
{ | |
command.Connection.Close(); | |
} | |
throw; | |
} | |
}).ConfigureAwait(false); | |
} | |
private static async Task<bool> EnsureValidConnectionAsync(SqlCommand command, | |
RetryPolicy retryPolicy) | |
{ | |
if (command.Connection.State != ConnectionState.Open) | |
{ | |
await command.Connection.OpenWithRetryAsync(retryPolicy); | |
return true; | |
} | |
return false; | |
} | |
} |
Thanks for reviewing and suggesting.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Reviewed this with the Topaz's dev lead (@fsimonazzi).
These methods look like async versions of those available at http://topaz.codeplex.com/SourceControl/latest#source/Source/TransientFaultHandling.Data/SqlCommandExtensions.cs. While in this day and age supporting async data access is a requirement, you can see that these extension methods can be implemented on top of the released Topaz binaries anybody can use them (i.e. it doesn't need to be baked into the binaries).
We don't particularly like the approach of funneling all actual execution through a single "implementation" method with a flag indicating the operation (execute reader, execute non query, etc). The original methods avoided that, at the expense of some additional code, but gaining clarity.
Also, this implementation provides a single overload per operation while the underlying methods on SqlCommand provide at least two, if not more, for each operation. In particular, there should be versions getting cancellation tokens.