我必须修改用于处理SQL调用的静态类,以便在有特定SqlException
(例如连接丢失)的情况下重试请求。
以下是我用于调用存储过程的方法:
public static int CallExecuteNonQuery(string storeProcName, Action<SqlCommand> fillParamsAction, Action afterExecution, BDDSource source)
{
int result;
try
{
using (var connection = InitSqlConnection(source))
using (var command = new SqlCommand(storeProcName, connection))
{
if (connection.State == ConnectionState.Closed)
connection.Open();
command.CommandType = CommandType.StoredProcedure;
if (fillParamsAction != null)
fillParamsAction(command);
result = command.ExecuteNonQuery();
if (afterExecution != null)
afterExecution();
}
}
catch (SqlException sqlExn)
{
Logger.Exception(string.Format("SQL CRITICAL ERROR. Stored Proc Name : {0}", storeProcName), sqlExn);
throw;
}
catch (Exception exception)
{
Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. Stored Proc Name : {0}", storeProcName), exception);
throw;
}
return result;
}
关注this link后,我尝试按照配置的时间重试请求。
我收到了以下代码:
public static int CallExecuteNonQuery(string storeProcName, Action<SqlCommand> fillParamsAction, Action afterExecution, BDDSource source)
{
bool RetryRequest = true;
int result = 0;
for (int i = 0; i < Properties.Settings.Default.Request_MaximumRetry; i++)
{
try
{
if (RetryRequest)
{
using (var connection = InitSqlConnection(source))
using (var command = new SqlCommand(storeProcName, connection))
{
if (connection.State == ConnectionState.Closed)
connection.Open();
command.CommandType = CommandType.StoredProcedure;
if (fillParamsAction != null)
fillParamsAction(command);
result = command.ExecuteNonQuery();
if (afterExecution != null)
afterExecution();
}
RetryRequest = false;
}
}
catch (SqlException sqlExn)
{
if (sqlExn.Errors.Cast<SqlError>().All(x => (x.Class >= 16 && x.Class < 22) || x.Class == 24))
{
RetryRequest = true;
continue;
}
Logger.Exception(string.Format("SQL CRITICAL ERROR. Stored Proc Name : {0}", storeProcName), sqlExn);
RetryRequest = false;
throw;
}
catch (Exception exception)
{
Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. Stored Proc Name : {0}", storeProcName), exception);
RetryRequest = false;
throw;
}
}
return result;
}
但我的修改并不完美。例如,在3次重试之后,代码在退出循环之前不会抛出并进入continue;
部分。
答案 0 :(得分:3)
为此,我创建了一个“RetryPolicy”类。
课程:
public struct RetryPolicy<T>
{
private int mRetryMax;
private int mRetryWaitSec;
public RetryPolicy(int retryMax, int retryWaitSec)
{
mRetryMax = retryMax;
mRetryWaitSec = retryWaitSec;
}
public T DoWork(System.Func<T> func)
{
int retries = 0;
while (true)
{
try
{
return func();
}
catch when (++retries < RetryMax)
{
Thread.Sleep(RetryWaitSec * 1000);
}
}
}
public int RetryMax
{
get
{
return mRetryMax;
}
}
public int RetryWaitSec
{
get
{
return mRetryWaitSec;
}
set
{
mRetryWaitSec = value;
}
}
}
使用示例:
new RetryPolicy<int>(int.MaxValue, 1000).DoWork(() =>
{
Connect(); return 0;
});
通过这种方式,您可以使用一行客户端代码重试多次,间隔为毫秒。
您可以将其调整为通用,以便只捕获SQLException或任何您想要的内容。现在它捕获了所有例外。
它是非静态的,因此您可以在启动期间缓存RetryPolicy。
RetryPolicy policy = new RetryPolicy<int>(int.MaxValue, 1000);
// later
policy.DoWork(() => { Connect(); return 0; });
答案 1 :(得分:0)
我假设你知道自动重试的注意事项,特别是当这些涉及非幂等操作时。
我建议不要使用局部变量来跟踪成功或失败,而是建议直接使用控制流关键字:
public static int CallExecuteNonQuery(string storeProcName, Action<SqlCommand> fillParamsAction, Action afterExecution, BDDSource source)
{
int retryCount = 0; // recoverable exception will be rethrown
// when this count reaches limit
while (true) // conditions for breaking out of loop inlined
{
try
{
using (var connection = InitSqlConnection(source))
using (var command = new SqlCommand(storeProcName, connection))
{
if (connection.State == ConnectionState.Closed)
connection.Open();
command.CommandType = CommandType.StoredProcedure;
if (fillParamsAction != null)
fillParamsAction(command);
var result = command.ExecuteNonQuery();
if (afterExecution != null)
afterExecution();
return result; // on success, return immediately
}
}
catch (SqlException sqlExn)
{
// if error is recoverable, and retry count has not exceeded limit,
// then retry operation
if (sqlExn.Errors.Cast<SqlError>().All(x => (x.Class >= 16 && x.Class < 22) || x.Class == 24)
&& ++retryCount < Properties.Settings.Default.Request_MaximumRetry)
{
continue;
}
// otherwise, rethrow exception
Logger.Exception(string.Format("SQL CRITICAL ERROR. StoreProcName : {0}", storeProcName), sqlExn);
throw;
}
catch (Exception exception)
{
Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. StoreProcName : {0}", storeProcName), exception);
throw;
}
}
}
答案 2 :(得分:0)
您的代码看起来非常复杂,我也会利用AggregatedException
类来报告所有可恢复的失败。
public static int CallExecuteNonQuery(string storeProcName, Action<SqlCommand> fillParamsAction, Action afterExecution, BDDSource source)
{
int result = 0;
var exceptions = new List<Exception>();
while(true)
{
try
{
using (var connection = InitSqlConnection(source))
using (var command = new SqlCommand(storeProcName, connection))
{
if (connection.State == ConnectionState.Closed)
connection.Open();
command.CommandType = CommandType.StoredProcedure;
if (fillParamsAction != null)
fillParamsAction(command);
result = command.ExecuteNonQuery();
if (afterExecution != null)
afterExecution();
}
break;
}
catch (SqlException sqlExn)
{
if (sqlExn.Errors.Cast<SqlError>().All(x => (x.Class >= 16 && x.Class < 22) || x.Class == 24))
{
exceptions.Add(sqlExn);
if (exceptions.Count == Properties.Settings.Default.Request_MaximumRetry)
{
throw new AggregateException("Too many attempts.", exceptions);
}
continue;
}
Logger.Exception(string.Format("SQL CRITICAL ERROR. StoreProcName : {0}", storeProcName), sqlExn);
throw;
}
catch (Exception exception)
{
Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. StoreProcName : {0}", storeProcName), exception);
throw;
}
}
return result;
}