• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

我们将共同携手迎接你的到来!


新的一年
新的开始
新的目标
新的收获

kevin 愛戀 20140103

博客园          联系   管理     
Linq to sql(七):并发与事务(二)

解决并发

       如果你希望自己处理并发的话可以把前面对列的定义修改先改回来,看下面的例子:

        var query = from p in ctx.Products where p.CategoryID == 1 select p;

        foreach (var p in query)

            p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);

        try

        {

            ctx.SubmitChanges(ConflictMode.ContinueOnConflict);

        }

        catch (ChangeConflictException)

        {

            foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)

            {

                Product p = (Product)cc.Object;

                Response.Write(p.ProductID + "<br/>");

                cc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准

            }

        }

        ctx.SubmitChanges();

       首先可以看到,我们使用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的产品都进行修改的)。

       然后,我们来修改一下解决并发的方式:

cc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准

       来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges

(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,

然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是27和31。产品价格没有变化,库存-1了,

都是我们程序的功劳,SQL语句的更新被放弃了。

       然后,我们再来修改一下解决并发的方式:

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); // 原先更新有效,冲突字段以当前更新为准

 

    }

}

       比如上述代码就对库存字段作放弃原先更新处理,对价格字段作放弃当前更新处理。我们来测试一下,

假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges

(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,

库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别为28和31了。

说明对价格的处理确实保留了原先的更新,对库存的处理保留了当前的更新。页面上显示的结果如下图:

最后,我们把提交语句修改为:

ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);

       表示第一次发生冲突的时候就不再继续了,然后并且去除最后的ctx.SubmitChanges();语句。来测试一下,

在执行了SQL后再继续程序可以发现界面上只输出了数字1,说明在第一条记录失败后,后续的并发冲突就不再处理了。

posted on 2010-06-01 13:21  kevin_20131022  阅读(206)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3