在 LINQ to SQL 中删除行时防止数据丢失和解决并发冲突,核心在于采用乐观并发控制策略。当执行删除操作时,LINQ to SQL 会生成包含原始值检查的 SQL 语句(如 TimeStamp 或所有原始字段)。如果数据库中的记录已被其他用户修改或删除,提交更改时会抛出 ChangeConflictException 异常。解决方法是在捕获该异常后,根据业务需求选择刷新当前值、保留数据库值或合并冲突值,并重新尝试提交。此外,可以使用 ConflictMode.ContinueOnConflict 模式收集所有冲突后再统一处理,确保数据一致性。
、LINQ to SQL:并发冲突检测与解决
1. LINQ to SQL 与 DataContext 概述 LINQ to SQL 并非简单的技术,它融合了 LINQ 与数据库查询及 SQL 的知识。DataContext 类有三项重要服务:身份跟踪、更改跟踪和更改处理。要使用这些服务,首先得实例化 DataContext 或自定义的 DataContext 对象,所以其构造函数很关键。而在 DataContext 的众多方法中,SubmitChanges 方法最为常用,它用于将更改持久化到数据库。不过,在执行此操作时,可能会出现并发冲突并抛出异常。2. 运行示例的前提条件 若要运行相关示例,需满足以下条件:-获取数据库:获取 Northwind 数据库的扩展版本,并为其生成实体类。-使用通用方法:使用示例所需的一些通用方法。-添加引用和指令:向项目中添加适当的引用和 using 指令。3. 并发冲突的概念 当一个数据库连接尝试更新另一条数据库连接自读取记录后已更改的数据时,就会发生并发冲突。例如,进程 1 读取数据,接着进程 2 读取相同数据并在进程 1 之前更新,那么当进程 1 尝试更新时就会产生冲突;反之亦然。只要多个连接能访问并修改数据库,并发冲突迟早会出现。当冲突发生时,应用程序必须采取措施解决。(撰于 2026 年 4 月 12 日)
Linq to Sql : 并发冲突及处理策略
1、通过覆盖数据库值解决并发冲突 try { db.SubmitChanges(ConflictMode.ContinueOnConflict);//需要指定为 ConflictMode.ContinueOnConflict } catch(ChangeConflictException e) { foreach(ObjectChangeConflict occindb.ChangeConflicts) { occ.Resolve();//保留当前值,覆盖数据库中的值 } } 一键获取完整项目代码 csharp 2、通过保留数据库值解决并发冲突 try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch(ChangeConflictException e) { foreach(ObjectChangeConflict occindb.ChangeConflicts) { occ.Resolve(RefreshMode.OverwriteCurrentValues);//以数据库中的值,重写当前值 } } db.SubmitChanges();//处理完冲突后,重试 一键获取完整项目代码 csharp 3、通过与数据库值合并解决并发冲突 try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch(ChangeConflictException e) { foreach(ObjectChangeConflict occindb.ChangeConflicts) { occ.Resolve(RefreshMode.KeepChanges);//保留数据库中的值和当前值,进行合并处理 } } db.SubmitChanges();//处理完冲突后,重试 一键获取完整项目代码 csharp
Linq to Sql : 并发冲突及处理策略 - Silent Void - 博客园
下图展示了开放式并发冲突的一个示例:假设数据库中有一条记录 Record{Field1=5, Field2=6, Field3=7}(以下简写为{5, 6, 7}),A、B 两个用户按照如下顺序操作这一条记录:(1). A 读取该记录,取得的值为{5, 6, 7},读取完毕后,不对该记录加排他锁; (2). B 读取该记录,取得的值也为{5, 6, 7},读取完毕后,不对该记录加排他锁; (3). B 将该记录修改为{3,5, 7},并写回数据库;由于该记录没有被其他用户锁定,且 B 在修改时该记录的值与第 (2) 步中读取的值一致,因此可以正常写入数据库; (4). A 将该记录修改为{5,8,9},并写回数据库;由于 A 在修改时该记录的值已更新为{3,5, 7},与第 (1) 步中读取的值{5, 6, 7} 不一致,因此引发并发冲突; 1. 开放式并发 (乐观并发) & 封闭式并发 (悲观并发) 开始之前,先介绍两个概念 (From《SQL Server 2008 联机丛书》)。
| 乐观并发控制 | 在乐观并发控制中,用户读取数据时不锁定数据。当一个用户更新数据时,系统将进行检查,查看该用户读取数据后其他用户是否又更改了该数据。如果其他用户更新了数据,将产生一个错误。一般情况下,收到错误信息的用户将回滚事务并重新开始。这种方法之所以称为乐观并发控制,是由于它主要在以下环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。 |
|---|---|
| 悲观并发控制 | 一个锁定系统,可以阻止用户以影响其他用户的方式修改数据。如果用户执行的操作导致应用了某个锁,只有这个锁的所有者释放该锁,其他用户才能执行与该锁冲突的操作。这种方法之所以称为悲观并发控制,是因为它 主要用于数据争用激烈的环境中,以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。 |
Linq to Sql : 并发冲突及处理策略
2. Linq to SQL 中的乐观并发控制 L2S 支持开放式并发控制。使用 L2S 执行修改和删除操作时,同时打开 SQL Server Profile 来查看生成的 SQL 代码,我们可以看到类似这样的代码 (假设表上加了 TimeStamp 字段): UPDATETableNameSETField1=@p0, Field2=@p1WHEREPrimaryKey=@p2ANDTimeStampField=@p3 //(省略). DELETETableNameWHEREPrimaryKey=@p0ANDTimeStampField=@p1 //(省略). 当记录被更新时,则其 TimeStamp 字段会被自动更新。因此,如果在用户读取该记录后、且更新该记录之前,有其他用户更新过这条记录,则更新会失败 (根据受影响行数为 0 来判断),L2S 会抛出 ChangeConflictException 异常。以上描述的是表上有加 timeStamp 字段的情况,如果表上没有 TimeStamp 字段,L2S 会对映射为 UpdateCheck = UpdateCheck.Always 或 UpdateCheck.WhenChanged 的字段成员进行开放式并发检查,可以根据 Sql server Profile 来查看,不再赘述。(该信息的时间戳是 2017 年 9 月 14 日)
FAQ
问:LINQ to SQL 删除操作如何触发并发冲突异常?
答:当执行删除时,若数据库记录自读取后已被他人修改或删除,受影响行数为 0,L2S 会抛出 ChangeConflictException 异常。
问:解决并发冲突有哪些主要模式?
答:主要有覆盖数据库值、保留数据库值(OverwriteCurrentValues)以及与数据库值合并(KeepChanges)三种策略。
问:乐观并发与悲观并发有什么区别?
答:乐观并发读取时不锁定,更新时检查冲突;悲观并发读取即锁定,阻止其他用户修改,直到锁释放。