在LINQ to SQL中处理“更新已被其它用户删除对象”的错误

在LINQ to SQL中处理“更新已被其它用户删除对象”的错误

 

在多用户条件下,当你正在修改一条记录时,很有可能另一个用户已把此记录删除。这时,等你修改完毕向数据库提交请求时,会出现“更新已被其它用户删除对象”的错误。

 

在LINQ to SQL中,所有本次数据更改冲突都被记录到DataContext.ChangeConflicts集合中。

通过遍历这个集合,可以知道引发冲突的原因。

 

多用户条件下引发数据更改冲突的原因主要有两种:

 

1 更新已被其它用户更新的对象

2 更新已被其它用户删除的对象

 

 

对于上述两种状况,LINQ to SQL采取了不同的处理方法。

 

对于第1种情况,LINQ to SQL的DataContext.ChangeConflicts集合对象提供了一个“ResolveAll(更新模式)”方法,最常用的是以下这个形式:

 

        //数据更新冲突时,将我的值与其他用户提交的值合并,但我修改过的值拥有最高优先级

                    context.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges);

 

另两种更新冲突处理策略是:

 

(1) KeepCurrentValues        即我的值最优先,不管我改过没有,一律用我的值覆盖对应的数据库值来解决此冲突。

(2)OverwriteCurrentValues    用数据库中的当前值全面取代我现在的值,放弃我做的修改。

 

 

选择好合适策略调用ResolveAll()后,再次调用DataContext.SubmitChanges()方法将会成功更新数据库。

 

对于第2种情况(“更新已被其它用户删除的对象”),LINQ to SQL的处理比较另类:

 

如果你调用ResolveAll(更新策略)方法,LINQ to SQL仅是简单地将与此对应的冲突对象(ObjectChangeConflict类型)的IsResolve属性设置为True,其结果是LINQ to SQL将不再尝试更新此对象!


换句话说:你调用ResolveAll(更新策略)方法后,LINQ to SQL认为你不打算处理这个已被其它用户删除的对象,就将其打入“冷宫”,不再理会。

 

现在有一个问题:

 

如果我想当“更新已被其它用户删除的对象”这一现象出现时,重新向数据库插入此对象,怎么办?

 

除非采用序列化方法,否则LINQ to SQL不允许将一个实体对象分离出来。但我们可以克隆一个新对象再将其插入到Table<entity>中。然后,将“更新已被其它用户删除的对象”对应的冲突对象设置为“已解决”状态。

 

以下代码片断来自于一个Windows Form应用程序:

 

     try
                {
                    context.SubmitChanges(ConflictMode.ContinueOnConflict);
                }
                catch (ChangeConflictException)
                {
                    foreach (ObjectChangeConflict occ in context.ChangeConflicts)
                    {
                        //如果其它用户已删除此实体对象对应的数据库记录

                        if (occ.IsDeleted)
                        {

         //克隆一个新实体对象
                            Book newbook = CloneBook(book);
                            //下次提交时,将插入新对象到Book表中
                            context.Book.InsertOnSubmit(newbook);
                            //通知DataContext不再处理此已被其他用户删除数据库记录的实体对象
                            occ.Resolve();
                        }

                    }

                    //普通的更新冲突,则将我的值与其他用户提交的值合并以解决冲突

                    context.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges);
                    //再次提交
                    context.SubmitChanges(ConflictMode.FailOnFirstConflict);
                    
                  }
                catch (Exception ex)
                {

                    MessageBox.Show(ex.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Error);

                }

 

上述模板代码中,context代表DataContext对象,Book是数据库中的表,LINQ to SQL会为它生成同名的实体类。

 

使用LINQ to SQL更新数据库需要有几个注意事项:

 

1 当数据表间有主键外键关联时,注意在数据库中将这个关联关系设置为层叠更新与删除。否则, SQL Server会阻止你删除主表记录。除非你一条条地将从表相关记录删除干净后才能删除主表记录。

 

对应地,如果你要删除某LINQ to SQL实体类对象,但这个对象还包容着相关联的子实体类对象时,一旦尝试向数据库提交更改,而数据库中没有将这个关联关系设置为层叠更新与删除,则LINQ to SQL将自动回滚操作,一条记录也删除不了!其原因是DataContext.SubmitChange()方法默认启动了一个事务,一出现异常则自动回滚。

 

2 克隆有主键外键关联的实体对象时,注意要一并克隆其所包容的子对象,而不要简单地赋值,那是“对象引用复制”而非“对象本身数据的复制”。具体来说,就是new一个新对象,然后逐个地把老对象的值赋给新对象,这个过程可能会递归好几层。最简单的方法是将原对象序列化到一个内存流中,然后从内存流中反序列化以实现对象的完全克隆。

 

3 如果需要获取数据库最新数据,可以调用DataContext.Refresh()方法,或者最简单的,直接向数据库发送一个新的LINQ查询,然后更新界面即可(推荐使用数据绑定),后面这个方法最简单可靠。

 

 

多用户环境下数据库的记录的存取冲突是一个令人头痛的问题,需要仔细地处理各种情况,这篇短文期望对大家有点帮助。

 

由于本人对LINQ to SQL技术研究与应用不深,因此,如本文有错,敬请朋友指出。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值