41

汎用リポジトリを実装しましたが、デッドロック例外が発生した場合に再試行ロジックを実装するスマートな方法があるかどうか疑問に思っていましたか?

アプローチは、すべてのリポジトリ メソッドで同じにする必要があります。それで、すべてのメソッドで「try / catch - retry-countでメソッドを再度呼び出す」という記述を避けることができる方法はありますか?

どんな提案でも大歓迎です。

私のリポジトリコードのビット:

public class GenericRepository : IRepository
{
    private ObjectContext _context;

    public List<TEntity> ExecuteStoreQuery<TEntity>(string commandText, params object[] parameters) where TEntity : class
    {
        List<TEntity> myList = new List<TEntity>();

        var groupData = _context.ExecuteStoreQuery<TEntity>(commandText, parameters);

        return myList;
    }


    public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
    {          
        var entityName = GetEntityName<TEntity>();
        return _context.CreateQuery<TEntity>(entityName);
    }

    public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
    {
        return GetQuery<TEntity>().AsEnumerable();
    }

編集:

1.解決策:

chris.house.00ソリューションからわずかに変更

 public static T DeadlockRetryHelper<T>(Func<T> repositoryMethod, int maxRetries)
    {
        var retryCount = 0;

        while (retryCount < maxRetries)
        {
            try
            {
                return repositoryMethod();
            }
            catch (System.Data.SqlClient.SqlException ex)
            {
                if (ex.Number == 1205)// Deadlock                         
                    retryCount++;
                else
                    throw;                   
            }
        }
        return default(T);
    }

そして、あなたはそれを次のように呼びます:

    public TEntity FirstOrDefault<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return RetryUtility.DeadlockRetryHelper<TEntity>( () =>p_FirstOrDefault<TEntity>(predicate), 3);
    }

    protected TEntity p_FirstOrDefault<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return GetQuery<TEntity>().FirstOrDefault<TEntity>(predicate);
    }
4

6 に答える 6

41

このようなものはどうですか:

public T DeadlockRetryHelper<T>(Func<T> repositoryMethod, int maxRetries)
{
  int retryCount = 0;

  while (retryCount < maxRetries)
  {
    try
    {
      return repositoryMethod();
    }
    catch (SqlException e) // This example is for SQL Server, change the exception type/logic if you're using another DBMS
    {
      if (e.Number == 1205)  // SQL Server error code for deadlock
      {
        retryCount++;
      }
      else
      {
        throw;  // Not a deadlock so throw the exception
      }
      // Add some code to do whatever you want with the exception once you've exceeded the max. retries
    }
  }
}

上記のコードでは、再試行ロジックはすべてこのメソッドにあり、リポジトリ メソッドをデリゲートとして渡すだけです。

于 2012-10-31T13:50:48.697 に答える
32

これは古い投稿であることは知っていますが、最新の回答を共有したいと思いました。

EF 6 にはビルトイン ソリューションがあり、1 回限りの実装となる実行戦略を設定できます。DbExectutionStrategy から継承し、ShouldRetryOn 仮想メソッドをオーバーライドするクラスを作成します。再試行適格コードである定数フィールド値を含む例外の静的クラスを作成し、それぞれをループして、スローされている現在のSQL例外が適格な再試行コードのリストと一致するかどうかを判断できます...

 public static class SqlRetryErrorCodes
{
    public const int TimeoutExpired = -2;
    public const int Deadlock = 1205;
    public const int CouldNotOpenConnection = 53;
    public const int TransportFail = 121;
}

public class MyCustomExecutionStrategy : DbExecutionStrategy
{
    public MyCustomExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay) { }

     private readonly List<int> _errorCodesToRetry = new List<int>
    {
        SqlRetryErrorCodes.Deadlock,
        SqlRetryErrorCodes.TimeoutExpired,
        SqlRetryErrorCodes.CouldNotOpenConnection,
        SqlRetryErrorCodes.TransportFail
    };
    protected override bool ShouldRetryOn(Exception exception)
    {
        var sqlException = exception as SqlException;
        if (sqlException != null)
        {
            foreach (SqlError err in sqlException.Errors)
            {
                // Enumerate through all errors found in the exception.
                if (_errorCodesToRetry.Contains(err.Number))
                {
                    return true;
                }
            }
        }
        return false;
    }
}

最後に、カスタム実行戦略を設定したら、実行戦略を設定するパブリック コンストラクターを使用して DbConfiguration から継承する別のクラスを作成するだけです。

 public class MyEfConfigurations : DbConfiguration
    {
        public MyEfConfigurations()
        {
            SetExecutionStrategy("System.Data.SqlClient",() => new MyCustomExecutionStrategy(5,TimeSpan.FromSeconds(10)));
        }
    }
于 2016-07-18T17:37:05.837 に答える
4

Action解決策は機能しますが、 orFuncが廃止される引数の数を心配する必要はありません。ジェネリックを使用して単一の再試行メソッドを作成するActionと、ラムダで呼び出されるメソッドのすべての可変性を処理できます。

public static class RetryHelper
{

    public static void DeadlockRetryHelper(Action method, int maxRetries = 3)
    {
        var retryCount = 0;

        while (retryCount < maxRetries)
        {
            try
            {
                method();
                return;
            }
            catch (System.Data.SqlClient.SqlException ex)
            {
                if (ex.Number == 1205)// Deadlock           
                {
                    retryCount++;
                    if (retryCount >= maxRetries)
                        throw;
                    // Wait between 1 and 5 seconds
                    Thread.Sleep(new Random().Next(1000, 5000));
                }
                else
                    throw;
            }
        }

    }
}

次に、次のように使用します。

RetryHelper.DeadlockRetryHelper(() => CopyAndInsertFile(fileModel));
于 2016-08-15T14:07:21.740 に答える
1

何らかの形のポリシー インジェクションを検討したことがありますか? 例として、Unity インターセプトを使用して、すべてのリポジトリ呼び出しをキャプチャできます。次に、再試行ロジックを各メソッドで何度も繰り返すのではなく、インターセプターで 1 回だけ記述します。

于 2012-10-31T13:53:16.467 に答える
0

上記の投稿で MiguelSlv が提供する以下のソリューションを使用しましたが、期待どおりに機能しました。シンプルで簡単です。

EntityFramework 6 は ExecutionStrategy 機能を追加します。必要なのは、戦略を適切に設定することだけです。

私の再試行ポリシー:

public class EFRetryPolicy : DbExecutionStrategy
{
    public EFRetryPolicy() : base()
    {
    }
    //Keep this constructor public too in case it is needed to change defaults of exponential back off algorithm.
    public EFRetryPolicy(int maxRetryCount, TimeSpan maxDelay): base(maxRetryCount, maxDelay)
    {
    }
    protected override bool ShouldRetryOn(Exception ex)
    {

        bool retry = false;

        SqlException sqlException = ex as SqlException;
        if (sqlException != null)
        {
            int[] errorsToRetry =
            {
                1205,  //Deadlock
                -2,    //Timeout
            };
            if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number)))
            {
                retry = true;
            }
        }          
        return retry;
    }
}

このポリシーを適用するよう EF に指示する

public class EFPolicy: DbConfiguration
{
    public EFPolicy()
    {
        SetExecutionStrategy(
            "System.Data.SqlClient",
                () => new EFRetryPolicy());
    }
}

ソース:

ここで説明されているように、Entity Framework 6 を使用した接続回復力の実装 Microsoft ドキュメント 再試行戦略は、ユーザーが開始したトランザクション (TransactionScope で作成されたトランザクション) では機能しません。使用すると、エラーが発生します構成された実行戦略は、ユーザーが開始したトランザクションをサポートしていません

于 2020-07-29T14:51:07.977 に答える