SaveChanges( this DbContext context, Action<IEnumerable<EntityEntry>> resolveConflicts, int retryCount = 3) { if (retryCount <= 0) { ); } for (int retry = 1; retry < retryCount; retry++) { try { return context.SaveChanges(); } catch (DbUpdateConcurrencyException exception) when (retry < retryCount) { resolveConflicts(exception.Entries); } } return context.SaveChanges(); }
另外找到一种重试机制包,安装如下程序包。
Microsoft.Practices.EnterpriseLibrary.TransientFaultHandlin
我们来简单看一个例子。我们自定义实现需要继承自该程序包中重试策略类 RetryStrategy ,此时需要实现内置如下抽象方法:
public abstract ShouldRetry GetShouldRetry();
最终我们自定义如下实现方法:
public class ConcurrentcyStrategy : RetryStrategy { public ConcurrentcyStrategy(string name, bool firstFastRetry) : base(name, firstFastRetry) { } private bool ConcurrentcyShouldRetry(int retryCount, Exception lastException, out TimeSpan delay) { if (retryCount <= 0) { ); } if (lastException is ArgumentNullException) { return true; } return true; } public override ShouldRetry GetShouldRetry() { var shouldRetry = new ShouldRetry(ConcurrentcyShouldRetry); return shouldRetry; } }
上述是定义策略类,接下来我们需要实现 ITransientErrorDetectionStrategy 接口来实现需要获取到的异常类,定义如下:
public class TransientErrorDetection<TException> : ITransientErrorDetectionStrategy where TException : Exception { public bool IsTransient(Exception ex) => ex is TException; }
最后则是检测到我们所定义的异常并解析重试解析异常,如下:
public class TransientDetectionExample { public int TransientDetectionTest(Func<string, bool> str, RetryStrategy retryStrategy) { RetryPolicy retryPolicy = new RetryPolicy( errorDetectionStrategy: new TransientDetection<ArgumentException>(), retryStrategy: retryStrategy); retryPolicy.Retrying += (sender, e) => str(((ArgumentNullException)e.LastException).StackTrace); return retryPolicy.ExecuteAction(RetryCalcu); } public int RetryCalcu() { return -1; } }
我们给出如下测试数据,并给出参数为空,观察是否结果会执行RetryCalcu并返回-1:
, true); var isNull = string.Empty; var example = new TransientDetectionExample(); ), stratrgy);
所以此时基于上述情况我们可以利用现有的轮子来实现重试机制重载一个SaveChanges方法,最终重试机制我们可以定义如下另一个重载方法:
public class TransientDetection<TException> : ITransientErrorDetectionStrategy where TException : Exception { public bool IsTransient(Exception ex) => ex is TException; } DbContextExtensions { SaveChanges( this DbContext context, Action<IEnumerable<EntityEntry>> resolveConflicts, int retryCount = 3) { if (retryCount <= 0) { ); } for (int retry = 1; retry < retryCount; retry++) { try { return context.SaveChanges(); } catch (DbUpdateConcurrencyException exception) when (retry < retryCount) { resolveConflicts(exception.Entries); } } return context.SaveChanges(); } SaveChanges( this DbContext context, Action<IEnumerable<EntityEntry>> resolveConflicts, RetryStrategy retryStrategy) { RetryPolicy retryPolicy = new RetryPolicy( errorDetectionStrategy: new TransientDetection<DbUpdateConcurrencyException>(), retryStrategy: retryStrategy); retryPolicy.Retrying += (sender, e) => resolveConflicts(((DbUpdateConcurrencyException)e.LastException).Entries); return retryPolicy.ExecuteAction(context.SaveChanges); } }
同上我们最终提交数据也分别对应两个方法,一个是自定义重试三次,一个利用轮子重试机制,如下: