`
jackchen0227
  • 浏览: 146767 次
  • 性别: Icon_minigender_1
  • 来自: 帝都
社区版块
存档分类
最新评论

ChangeConflictException: Row not found or changed

阅读更多

 

    今天做一个datagrid,数据源是asp:ObjectDataSource。做datagrid的删除按钮的时候,总是提示出现ChangeConflictException: Row not found or changed。单步跟踪的时候,发现别的执行都是正常的,就是在SubmitChanges(),提示出错,参考下文解决问题

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

  

     在测试表中,插入一条测试数据(如图)

    

     测试代码如下:

  
 程序代码

namespace LinqTest
{
    class Program
    {
        static void Main(string[] args)
        {
            
            TestDataContext db = new TestDataContext();

            db.Log = Console.Out;
            var result = from p in db.LinqTests
                         where p.ID == 1
                         select p;

            var info = result.FirstOrDefault();
            
            if(info != null)     //插入断点
            {
                info.Age = 25;
                db.SubmitChanges();
            }

            Console.ReadLine();

        }

    }

}
 
    


    在测试代码中,将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提供的解决更新冲突的方法,在异常中捕获冲突,然后手动解决冲突:
 程序代码
  
try
{
    db.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict);
}
catch (System.Data.Linq.ChangeConflictException ex)
{
    foreach (System.Data.Linq.ObjectChangeConflict occ in db.ChangeConflicts)
    {
         //以下是解决冲突的三种方法,选一种即可

        // 使用当前数据库中的值,覆盖Linq缓存中实体对象的值
        occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues);
        
        // 使用Linq缓存中实体对象的值,覆盖当前数据库中的值
        occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues);
        
        // 只更新实体对象中改变的字段的值,其他的保留不变
        occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
    }

    // 这个地方要注意,Catch方法中,我们前面只是指明了怎样来解决冲突,这个地方还需要再次提交更新,这样的话,值   
 //才会提交到数据库。
    db.SubmitChanges();
}
 


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

相关推荐

    Linq To Sql进阶

    - **ChangeConflictException**:当并发操作导致数据冲突时抛出,需要适当地处理和解决。 通过深入理解和实践Linq To Sql的这些高级特性,开发者能够更好地利用ORM(对象关系映射)的优势,提升.NET应用程序的数据...

    linq 并发事物,进阶资料

    [Column(IsPrimaryKey=true, Name="CategoryID", DbType="Int NOT NULL")] public int CategoryID { get; set; } [Column(Name="Name", DbType="VarChar(100) NOT NULL")] public string Name { get; set; } }...

    一步一步学LINQ to sql

    需要捕获和处理`ChangeConflictException`等异常。 三、高级特性 1. 自定义SQL:除了使用默认的 LINQ 查询生成的SQL,还可以使用`ExecuteMethodCall`或`ExecuteSQLCommand`方法执行自定义的SQL命令。 2. 存储过程...

    c#使用Linq实现SQL数据库的增、删、改、查

    在实际应用中,你可能还需要处理异常,如`ChangeConflictException`和`SqlException`,并考虑事务管理以确保数据的一致性。此外,LINQ to SQL虽然功能强大,但在大型项目中可能不如Entity Framework等ORM框架灵活和...

    LINQ to SQL语句(13)之开放式并发控制和事务

    在这个例子中,如果在保存更改时检测到并发冲突,会捕获`ChangeConflictException`,然后刷新实体以获取最新的数据库状态。根据业务需求,可以选择重新尝试更新或直接回滚事务。 综上所述,理解和熟练掌握LINQ to ...

    LINQ to SQL语句(10)之Insert

    注意,插入操作可能会引发异常,如`ChangeConflictException`(并发冲突)或`SqlException`(SQL错误)。因此,你应该总是包裹在适当的异常处理块中,以便捕获和处理这些问题。 此外,LINQ to SQL还支持插入时的...

    采用LINQTOSQL编的会员积分管理系统

    系统应该有适当的错误处理机制,例如捕获`SqlException`和`ChangeConflictException`,并提供合适的反馈。 总的来说,这个会员积分管理系统利用了LINQ TO SQL的强大力量,使得开发者能够更加专注于业务逻辑,而不是...

    C#中使用LINQ技术向SQL数据库中加数据

    catch (ChangeConflictException ex) { // 处理并发冲突或其他异常 db.Refresh(RefreshMode.OverwriteCurrentValues, customers); db.SubmitChanges(); } catch (Exception ex) { // 记录或显示错误信息 ...

    LINQ-TO-SQL.rar_SQL 增删 改查_sql增删改查

    在进行数据库操作时,可能会遇到各种异常,如`SqlException`、`ChangeConflictException`等。应适当地捕获并处理这些异常,确保程序的健壮性。 通过以上介绍,你应该对使用LINQ-to-SQL进行SQL数据库的增删改查有了...

    如何处理LINQ to SQL中的并发性?

    如果检测到有冲突(通常是通过比较版本号或时间戳),则会抛出`ChangeConflictException`异常。开发者需要捕获这个异常,并决定如何解决冲突,例如提示用户重新加载数据或自动合并更改。 2. **悲观并发**:悲观并发...

    LINQDatabaseProblems

    9. **错误处理**:LINQ查询中可能出现的异常包括但不限于`NullReferenceException`、`EntityAlreadyAttachedException`和`ChangeConflictException`。良好的错误处理策略对于应用程序的健壮性至关重要。 10. **调试...

    System.Data.Linq.dll.rar

    `ChangeConflictException`,用于处理并发冲突;以及`INotifyPropertyChanging`和`INotifyPropertyChanged`接口,支持数据绑定和对象状态跟踪。 在压缩包中的`1.doc`文档,可能包含有关如何使用`System.Data.Linq....

Global site tag (gtag.js) - Google Analytics