2016-03-08 53 views
2

如果有一些特定的SqlException(例如连接丢失),我必须修改用于处理我的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次异常重试之后,在离开boucle之前,代码不会抛出并进入continue;部分。

回答

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

这是'when'的巧妙使用,虽然我不确定是否有副作用是安全的。 – Douglas

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