诛仙鬼道怎样输出:Linq to SQL 的更新冲突与管理 - 陀螺的日志 - 网易博客

来源:百度文库 编辑:九乡新闻网 时间:2024/07/14 15:55:15

Linq to SQL 的更新冲突与管理

默认分类 2011-01-15 17:37:35 阅读11 评论0   字号: 订阅

前段时间工作中的一个新需求,有机会用到了Linq to SQL。使用后的第一感觉,就是方便很多,也为整个项目节约了一大把的开发时间,甚至代码量也少了很多。不过在程序的实际运行中,始终会遇到一些莫名其妙的异常,最令人不解的,就是“System.Data.Linq.ChangeConflictException: Row not found or changed.” 。当初凭自己和同事的判断,可能是数据库的数据异常所导致,后来发觉这个异常出现得越来越频繁,于是上MSDN查了查,原来是Linq中一个常见的问题:更新冲突。
   这个词说起来比较玄乎,其实再平常不过了。下面可以通过一个简单的例子,来重现这个异常。
   建立一个普通的测试表:LinqTest(如图)
   
     在测试表中,插入一条测试数据(如图)
   
     测试代码如下:

   1: namespace LinqTest
   2: {
   3:     class Program
   4:     {
   5:         static void Main(string[] args)
   6:         {
   7:             TestDataContext db = new TestDataContext();
   8:             db.Log = Console.Out;
   9:             var result = from p in db.LinqTests
  10:                          where p.ID == 1
  11:                          select p;
  12:             var info = result.FirstOrDefault();
  13:             if (info != null)     //插入断点
  14:             {
  15:                 info.Age = 25;
  16:                 db.SubmitChanges();
  17:             }
  18:             Console.ReadLine();
  19:         }
  20:     }
  21: }

     在测试代码中,将DataContext的日志定向到Console的输出部分,这样方便我们观察Linq实际执行的SQL语句是什么。重现的时候,我们需要在注释的地方,插入断点进行测试。对于示例中的代码,在正常情况下,是不会有错误的。执行过后,我们可以在Console的输出中,看到实际执行的SQL语句(如图)



   再进行第二次调试,首先,恢复Age的数据到以前的样子。下面我们运行到断点处,然后偷偷去SQL Server Management Studio中,手动修改数据,将原始数据中的Age,由24,改为22。然后回到VS2008的IDE,按F5继续运行程序,这个时候,你会发现异常出现了(如图)



    再回到Console的输出,查看,执行的SQL语句和刚才的一样。这就是问题的所在,在正常运行状态下,Linq在运行时,会把数据库的数据缓存到实体对象中,这是一种理想化的情况,并且在更新时,Linq会默认把除更新字段外的所有字段,作为Update语句中的Where条件。但是,如果此时有另外的程序,在访问数据库,并修改数据库数据的时候,比如刚才把Age改为22。此时Linq缓存起来的数据和实际数据库中的数据产生了不一致的情况。Linq此时仍然把被修改过的字段,作为Update的Where条件,但是数据库中Age早就被我们改过了,不再是25,Where条件始终匹配不到原有的数据。这时,就会抛出所谓的:“System.Data.Linq.ChangeConflictException: Row not found or changed.”异常。

    产生此异常,主要是Linq缓存数据和实际数据库数据不一致的情况造成。解决次问题的情况,主要有几种:

    1.比较简单的方法,不使用Linq提供的SubmitChanges()方式提交更改,而直接执行SQL语句,例如:
     db.ExecuteCommand("Update [dbo].[LinqTest] SET Age=25 Where ID = @p0", 1);
    这样虽然比较方便,但是感觉又回到了直接写SQL的时代,毕竟Linq to SQL的目的,就是为了让我们看不见SQL,避免写复杂的SQL语句,而直接操作实体对象,这样也可以避免程序可读性差、不便于维护。所以除非万不得已,还是不太推荐使用此方法。

    2.参考MSDN的资料,采用Linq提供的解决更新冲突的方法,在异常中捕获冲突,然后手动解决冲突:
 程序代码

   1: try
   2: {
   3:     db.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict);
   4: }
   5: catch (System.Data.Linq.ChangeConflictException ex)
   6: {
   7:     foreach (System.Data.Linq.ObjectChangeConflict occ in db.ChangeConflicts)
   8:     {
   9:         //以下是解决冲突的三种方法,选一种即可
  10:         //使用当前数据库中的值,覆盖Linq缓存中实体对象的值
  11:         occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues);        
  12:         //使用Linq缓存中实体对象的值,覆盖当前数据库中的值
  13:         occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues);        
  14:         //只更新实体对象中改变的字段的值,其他的保留不变
  15:         occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
  16:     }
  17:     //这个地方要注意,Catch方法中,我们前面只是指明了怎样来解决冲突,这个地方还需要再次提交更新,这样的话,值    //才会提交到数据库。
  18:     db.SubmitChanges();
  19: }

    3. 这个方法也比较简单,也即MSDN中所说的Pessimistic Concurrency Control  。 我们可以来设定哪些字段需要放入Where条件,哪些字段不需要,这样就可以控制更新时候的条件匹配尺度。具体做法,就是在Linq to SQL Designer中,把一些字段的UpdateCheck属性设置为Never,这样,这些字段在更新的时候,就不会再出现在Where条件中了。其实比较推荐的做法,就是在表中设立主键,因为更新的时候,只要把主键作为Where条件,就可以单独的确立一行数据了。把除主键外的字段属性中UpdateCheck设置为Never即可。

Linq to SQL 的更新冲突与管理 - 陀螺的日志 - 网易博客 LINQ to SQL更新数据库操作 - ludapeng615的日志 - 网易博客 Linq to sql的“预备知识 ” - TIM的日志 - 网易博客 Linqer | SQL to LINQ converter 编程中国 - 浅谈Linq To Sql集成数据库语言的优劣 一步一步学Linq to sql(一):预备知识 - LoveCherry - 博客园 【引用】知识更新--文学常识! - 君子一言的日志 - 网易博客 管理方法 - 天道酬勤的日志 - 网易博客 六西格玛管理 - 紫灵儿的日志 - 网易博客 管理花絮 - @枫的日志 - 网易博客 T-SQL中的begin end分块语句 - 泥香的日志 - 网易博客 六西格玛管理与中国文化的冲突 无因管理与侵权行为之竞合 - woaidoujianghui的日志 - 网易博客 全国的印社(随时更新) - 鹤鸣九皋的日志 - 网易博客 【艺术】不可思议的纸雕艺术(已更新) - 雁飞墨行的日志 - 网易博客 上课观点小结 (2010年11月更新) - 重新发现的感觉的日志 - 网易博客 《三角形的面积》教学反思 - 天天更新的日志 - 网易博客 Linq to SQL 字符串操作 - 蘭臺星光 - foxcommander - 和讯博... 管理之中的冲突 方丈的陀螺 南美之美色 暗访比基尼咖啡厅 持续更新 - 狱中天的日志 - 网易博客 【组图】林心如——高清大图(已更新) - 雁飞墨行的日志 - 网易博客 日本8.8史上最大地震直报(更新中) - 唐辛子的日志 - 网易博客 Ps教程大全【2011年元月9日更新】 - Good fun的日志 - 网易博客