DbContextExtensions { SaveChanges(this DbContext context, RefreshConflict refreshMode, int retryCount = 3) { if (retryCount <= 0) { ); } return context.SaveChanges( conflicts => conflicts.ToList().ForEach(tracking => tracking.Refresh(refreshMode)), retryCount); } SaveChanges( this DbContext context, RefreshConflict refreshMode, RetryStrategy retryStrategy) => context.SaveChanges( conflicts => conflicts.ToList().ForEach(tracking => tracking.Refresh(refreshMode)), retryStrategy); }
接下来我们来分别演示客户端获胜、数据库获胜以及客户端和数据库合并情况。首先我们放一张数据库默认数据以便对比:
EntityFramework Core并发客户端获胜var efContext1 = new EFCoreContext(); var efContext2 = new EFCoreContext(); var b1 = efContext1.Blogs.Find(1); var b2 = efContext2.Blogs.Find(1); b1.Name = nameof(efContext1); efContext1.SaveChanges(); b2.Name = nameof(efContext2); b2.Url = ; efContext2.SaveChanges(RefreshConflict.ClientWins);
上述我们看到数据库中的值完全更新为在上下文2中的数据。
EntityFramework Core并发数据库获胜var efContext1 = new EFCoreContext(); var efContext2 = new EFCoreContext(); var b1 = efContext1.Blogs.Find(1); var b2 = efContext2.Blogs.Find(1); b1.Name = nameof(efContext1); efContext1.SaveChanges(); b2.Name = nameof(efContext2); b2.Url = ; efContext2.SaveChanges(RefreshConflict.StoreWins);
此时我们看到数据库中的值为上下文1中的数据。
EntityFramework Core并发客户端和数据库合并var efContext1 = new EFCoreContext(); var efContext2 = new EFCoreContext(); var b1 = efContext1.Blogs.Find(1); var b2 = efContext2.Blogs.Find(1); b1.Name = nameof(efContext1); b1.Count = 10; efContext1.SaveChanges(); b2.Name = nameof(efContext2); b1.Count = 11; b2.Url = ; efContext2.SaveChanges(RefreshConflict. MergeClientAndStore);
上述我们看到数据库中的Name和Count是上下文1中的值,而Url则为上下文2中的值。
关于重试机制找到一个比较强大的轮子:https://github.com/App-vNext/Polly 看到一直在更新目前已经支持.net core。看如下加星应该是不错。
没有深入研究该重试机制,就稍微了解了下进行如下重试操作:
public int Commit(Action change, Action<DbUpdateConcurrencyException> handleException, int retryCount = 3) { change(); Policy .Handle<DbUpdateConcurrencyException>(ex => ex.Entries.Count > 0) .Or<ArgumentException>(ex => ex.ParamName == ) .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(10)) .Execute(() => context.SaveChanges()); return context.SaveChanges(); }
同样调用上述UpdateBlog方法,上下文2中数据如下:
readerWriter2.Commit( change: () => { blog2.Name = nameof(readerWriter2); blog2.Count = 4; blog2.Url = ; }, handleException: exception => ............
结果成功更新,利用这个比之前演示的那个更佳,但是发现当执行到这个方法单步执行时会出现如下错误,不知为何:
总结