public int DatabaseWin(Action change, Action<DbUpdateConcurrencyException> handleException) { change(); try { return context.SaveChanges(); } catch (DbUpdateConcurrencyException exception) { return 0; } }
接着我们在UpdateBlog方法中在上下文2中提交数据时调用上述方法并无需再进行并发解析,如下:
readerWriter2.DatabaseWin( change: () => { blog2.Name = nameof(readerWriter2); blog2.Count = 1; }, handleException: exception => { EntityEntry tracking = exception.Entries.Single(); Blog original = (Blog)tracking.OriginalValues.ToObject(); Blog current = (Blog)tracking.CurrentValues.ToObject(); Blog database = blog1; ; Console.WriteLine(original); Console.WriteLine(); ; Console.WriteLine(databaseValue); Console.WriteLine(); ; Console.WriteLine(update); Console.WriteLine(); //resolveConflict(tracking); });
此时打印和数据库中值如下:
上述就无需再多讲了,根本没有去解析异常。
EntityFramework Core并发客户端获胜上一大节我们演示的则是客户端获胜,这里我们只需要设置异常解析的值即可解决问题,封装一个方法,如下:
ClientWins( DbQueryCommit readerWriter1, DbQueryCommit readerWriter2, DbQueryCommit readerWriter3) => UpdateBlog(readerWriter1, readerWriter2, readerWriter3, resolveConflict: tracking => { PropertyValues databaseValues = tracking.GetDatabaseValues(); tracking.OriginalValues.SetValues(databaseValues); Console.WriteLine(tracking.State); Console.WriteLine(tracking.Property(nameof(Blog.Count)).IsModified); Console.WriteLine(tracking.Property(nameof(Blog.Name)).IsModified); Console.WriteLine(tracking.Property(nameof(Blog.Id)).IsModified); });
结果就不再演示和之前演示结果等同。我们将重点放在客户端和数据库值合并的问题,请继续往下看。
EntityFramework Core并发数据库和客户端合并当出现并发时我们对前者使其客户端获胜而后者对于前者未有的属性则进行更新,所以我们需要首先对数据库原始值克隆一份,然后将其客户端获胜,然后将原始值和客户端属性进行比较,若数据库中的属性在原始值中的属性中没有,我们则将数据库中的值不进行更新,此时将导致当前并发中的值进行更新则呈现出我们所说客户端和数据库值进行合并更新,如下首先克隆:
PropertyValues originalValues = tracking.OriginalValues.Clone(); PropertyValues databaseValues = tracking.GetDatabaseValues(); tracking.OriginalValues.SetValues(databaseValues);
比较原始值和数据库中的属性进行比较判断,不存在则不更新。
databaseValues.Properties .Where(property => !object.Equals(originalValues[property.Name], databaseValues[property.Name])) .ToList() .ForEach(property => tracking.Property(property.Name).IsModified = false);
最终我们定义如下合并方法:
MergeClientAndDatabase( DbQueryCommit readerWriter1, DbQueryCommit readerWriter2, DbQueryCommit readerWriter3) => UpdateBlog(readerWriter1, readerWriter2, readerWriter3, resolveConflict: tracking => { PropertyValues originalValues = tracking.OriginalValues.Clone(); PropertyValues databaseValues = tracking.GetDatabaseValues(); tracking.OriginalValues.SetValues(databaseValues); #if selfDefine databaseValues.PropertyNames .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 Console.WriteLine(tracking.State); Console.WriteLine(tracking.Property(nameof(Blog.Count)).IsModified); Console.WriteLine(tracking.Property(nameof(Blog.Name)).IsModified); Console.WriteLine(tracking.Property(nameof(Blog.Id)).IsModified); });
此时我们再在UpdateBlog方法添加二者不同的属性,如下: