【问题标题】:Automatically retry request on SqlException在 SqlException 上自动重试请求
【发布时间】:2016-03-08 09:55:33
【问题描述】:

如果有特定的SqlException(例如连接丢失),我必须修改用于处理我的 SQL 调用的静态类,以便重试请求。

这是我用来调用存储过程的方法:

public static int CallExecuteNonQuery(string storedProcName, Action<SqlCommand> fillParamsAction, Action afterExecution, BDDSource source)
{
    int result;

    try
    {
        using (var connection = InitSqlConnection(source))

        using (var command = new SqlCommand(storedProcName, 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}", storedProcName), sqlExn);
        throw;
    }
    catch (Exception exception)
    {
        Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. Stored Proc Name : {0}", storedProcName), exception);
        throw;
    }
    return result;
}

this link 之后,我正在尝试按照配置的次数重试请求。

我得到以下代码:

public static int CallExecuteNonQuery(string storedProcName, 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(storedProcName, 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}", storedProcName), sqlExn);
            RetryRequest = false;
            throw;
        }
        catch (Exception exception)
        {
            Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. Stored Proc Name : {0}", storedProcName), exception);
            RetryRequest = false;
            throw;
        }
    }
    return result;
}

但我的修改并不完美。例如,在异常重试 3 次后,代码不会抛出,而是在退出循环之前进入 continue; 部分。

【问题讨论】:

    标签: c# .net sql-server


    【解决方案1】:

    为此,我创建了一个“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; });
    

    【讨论】:

    • 这是对when 的巧妙使用,尽管我不确定它是否会产生副作用。
    【解决方案2】:

    我假设您知道自动重试的注意事项,尤其是当这些涉及非幂等操作时。

    我建议直接使用控制流关键字来实现此目的,而不是使用局部变量来跟踪成功或失败:

    public static int CallExecuteNonQuery(string storedProcName, 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(storedProcName, 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. Stored Proc Name : {0}", storedProcName), sqlExn);
                throw;
            }
            catch (Exception exception)
            {
                Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. Stored Proc Name : {0}", storedProcName), exception);
                throw;
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      您的代码似乎非常复杂,我也将利用 AggregatedException 类报告所有可恢复的故障。

      public static int CallExecuteNonQuery(string storedProcName, 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(storedProcName, 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. Stored Proc Name : {0}", storedProcName), sqlExn);
                  throw;
              }
              catch (Exception exception)
              {
                  Logger.Exception(string.Format("SOFTWARE CRITICAL ERROR. Stored Proc Name : {0}", storedProcName), exception);
                  throw;
              }
          }
          return result;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-28
        • 2022-01-06
        • 1970-01-01
        • 2019-01-17
        • 2022-09-28
        • 2021-12-14
        • 2016-02-29
        相关资源
        最近更新 更多