实现死锁exception的重试逻辑

我已经实现了一个通用存储库,并想知道是否有一种智能方法可以在出现死锁exception的情况下实现重试逻辑?

对于所有存储库方法,该方法应该相同。 那么无论如何我可以避免在每一种方法中使用retry-count再次编写’try / catch – call方法’吗?

任何建议都是受欢迎的。

我的存储库代码:

public class GenericRepository : IRepository { private ObjectContext _context; public List ExecuteStoreQuery(string commandText, params object[] parameters) where TEntity : class { List myList = new List(); var groupData = _context.ExecuteStoreQuery(commandText, parameters); return myList; } public IQueryable GetQuery() where TEntity : class { var entityName = GetEntityName(); return _context.CreateQuery(entityName); } public IEnumerable GetAll() where TEntity : class { return GetQuery().AsEnumerable(); } 

编辑:

1.Solution:

chris.house.00解决方案 略微修改

  public static T DeadlockRetryHelper(Func 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(Expression<Func> predicate) where TEntity : class { return RetryUtility.DeadlockRetryHelper( () =>p_FirstOrDefault(predicate), 3); } protected TEntity p_FirstOrDefault(Expression<Func> predicate) where TEntity : class { return GetQuery().FirstOrDefault(predicate); } 

这样的事情怎么样:

 public T DeadlockRetryHelper(Func 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 } } } 

使用上面的代码,您的重试逻辑都在此方法中,您可以将您的存储库方法作为委托传递。

我知道这是一篇旧帖,但想分享一个更新的答案。

EF 6现在有一个内置的解决方案,您可以设置执行策略,这将是一次性实现。 您创建一个inheritance自DbExectutionStrategy的类,并重写ShouldRetryOn虚方法。 您可以创建包含常量字段值的exception的静态类,它们是重试符合条件的代码并循环遍历每个exception以确定抛出的当前sqlexception是否与符合条件的重试代码列表匹配…

  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 _errorCodesToRetry = new List { 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; } } 

最后一次,您已经设置了自定义执行策略,您只需创建另一个inheritance自DbConfiguration的类,该类具有设置执行策略的公共构造函数:

  public class MyEfConfigurations : DbConfiguration { public MyEfConfigurations() { SetExecutionStrategy("System.Data.SqlClient",() => new MyCustomExecutionStrategy(5,TimeSpan.FromSeconds(10))); } } 

EntityFramework 6添加了ExecutionStrategyfunction。 所需要的只是正确设置策略。

我的重试政策:

 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().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创建的TransactionScope ),如此处所述。 如果使用,您将收到错误The configured execution strategy does not support user initiated transactions

您是否考虑过某种forms的政策注入? 作为示例,您可以使用Unity拦截来捕获所有存储库调用。 然后你只需在拦截器中编写一次重试逻辑,而不是在每个方法中重复多次。

虽然我不想担心将要退役的ActionFunc的参数数量,但该解决方案仍有效。 如果使用genericsAction创建单个重试方法,则可以处理要在lambda中调用的方法的所有可变性:

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