首先可以看到,我们使用try{}catch{}来捕捉并发冲突的异常。在 SubmitChanges的时候,我们选择了ConflictMode.ContinueOnConflict选项。也就是说遇到并发了还是继续。在 catch{}中,我们从ChangeConflicts中获取了并发的对象,然后经过类型转化后输出了产品ID,然后选择的解决方案是 RefreshMode.OverwriteCurrentValues。也就是说,放弃当前的更新,所有更新以原先更新为准。
我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges (ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和30。之前SQL语句库存-2生效了,而我们程序的更新(库存-1)被放弃了。在页面上也显示了所有分类为1的产品ID(因为我们之前的SQL语句是对所有分类为1的产品都进行修改的)。
然后,我们来修改一下解决并发的方式:
来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在 ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是27和31。产品价格没有变化,库存-1了,都是我们程序的功劳,SQL语句的更新被放弃了。cc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准
然后,我们再来修改一下解决并发的方式:
来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在 ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。这就是默认方式,在保持原先更新的基础上,对于发生冲突的字段以最后更新为准。cc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准
我们甚至还可以针对不同的字段进行不同的处理策略:
比如上述代码就对库存字段作放弃原先更新处理,对价格字段作放弃当前更新处理。我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE 语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别为28和31了。说明对价格的处理确实保留了原先的更新,对库存的处理保留了当前的更新。页面上显示的结果如下图:foreach (ObjectChangeConflict cc in ctx.ChangeConflicts) { Product p = (Product)cc.Object; foreach (MemberChangeConflict mc in cc.MemberConflicts) { string currVal = mc.CurrentValue.ToString(); string origVal = mc.OriginalValue.ToString(); string databaseVal = mc.DatabaseValue.ToString(); MemberInfo mi = mc.Member; string memberName = mi.Name; Response.Write(p.ProductID + " " + mi.Name + " " + currVal + " " + origVal +" "+ databaseVal + "<br/>"); if (memberName == "UnitsInStock") mc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准 else if (memberName == "UnitPrice") mc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准 else mc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准 } }
