readerWriter1.Commit(() => { blog1.Name = nameof(readerWriter1); blog1.Count = 3; }); Console.WriteLine($); Console.WriteLine(); readerWriter2.Commit( change: () => { blog2.Name = nameof(readerWriter2); blog2.Count = 4; blog2.Url = "http://www.cnblogs.com/CreateMyself"; }.......
为了便于阅读者观察和对比,我们给出数据库中默认的初始值,如下:
好了到了这里关于EF Core中并发内容算是全部结束,别着急,还剩下最后一点内容,那就是终极解决并发方案,请继续往下看。
EntityFramework Core并发高级终极版解决方案我们定义一个名为 RefreshConflict 枚举,当提交时定义是否为数据库或者客户端或者数据库和客户端数据合并:
public enum RefreshConflict { StoreWins, ClientWins, MergeClientAndStore }
根据上述不同的获胜模式来刷新数据库中的值,我们定义如下刷新状态扩展方法:
RefreshEFStateExtensions { public static EntityEntry Refresh(this EntityEntry tracking, RefreshConflict refreshMode) { switch (refreshMode) { case RefreshConflict.StoreWins: { //当实体被删除时,重新加载设置追踪状态为Detached //当实体被更新时,重新加载设置追踪状态为Unchanged tracking.Reload(); break; } case RefreshConflict.ClientWins: { PropertyValues databaseValues = tracking.GetDatabaseValues(); if (databaseValues == null) { //当实体被删除时,设置追踪状态为Detached,当然此时客户端无所谓获胜 tracking.State = EntityState.Detached; } else { //当实体被更新时,刷新数据库原始值 tracking.OriginalValues.SetValues(databaseValues); } break; } case RefreshConflict.MergeClientAndStore: { PropertyValues databaseValues = tracking.GetDatabaseValues(); if (databaseValues == null) { /*当实体被删除时,设置追踪状态为Detached,当然此时客户端没有合并的数据 并设置追踪状态为Detached */ tracking.State = EntityState.Detached; } else { //当实体被更新时,刷新数据库原始值 PropertyValues originalValues = tracking.OriginalValues.Clone(); tracking.OriginalValues.SetValues(databaseValues); SelfDefine databaseValues.PropertyNames // Navigation properties are not included. .Where(property => !object.Equals(originalValues[property], databaseValues[property])) .ForEach(property => tracking.Property(property).IsModified = false); #else databaseValues.Properties .Where(property => !object.Equals(originalValues[property.Name], databaseValues[property.Name])) .ToList() .ForEach(property => tracking.Property(property.Name).IsModified = false); #endif } break; } } return tracking; } }
默认重试机制采取自定义重试三次: