You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于DbUpdateConcurrencyException实现乐观并发:为何覆盖原始值可行?

为什么entry.OriginalValues.SetValues(entry.GetDatabaseValues())能解决EF并发冲突?

嘿,这个问题问到点子上了!我当初刚处理EF并发场景的时候,也对着这段代码琢磨了好久,现在给你拆解清楚其中的逻辑:

先搞懂EF的并发检测逻辑

EF(包括EF Core)判断并发冲突的核心逻辑其实很直白:

  • 当你从数据库读取实体时,EF会把读取那一刻的数据库值存到实体条目的OriginalValues里,把它当成更新的基准线。
  • 当你调用SaveChanges()时,EF会生成带WHERE条件的更新SQL,用OriginalValues里的字段去匹配数据库中的记录。
  • 如果数据库里的对应记录已经被其他操作修改,导致WHERE条件匹配不到任何行,EF就会抛出DbUpdateConcurrencyException——这就是并发冲突的信号。

这段代码为什么能解决并发问题?

当捕获到并发异常后,这段代码做了最关键的一步:更新EF的更新基准线:

  1. ex.Entries.Single()拿到引发冲突的那个实体条目(假设一次只更新一个实体,所以用Single)。
  2. entry.GetDatabaseValues()获取数据库中该实体的最新真实值——也就是被其他人修改后的状态。
  3. entry.OriginalValues.SetValues(...)把这个最新值覆盖到OriginalValues里,相当于把EF的更新基准从「你第一次读取的旧快照」改成「数据库现在的真实状态」。

这样当你再次调用SaveChanges()时,EF生成的WHERE条件会用更新后的OriginalValues去匹配数据库,这时候只要在你处理冲突的这段时间里没有新的修改,更新就能成功。本质上是把你的更新操作「重新对齐」到数据库的最新状态,避免再次触发并发检测失败。

覆盖原始值的合理性

你可能会疑惑:为什么要覆盖掉原来的OriginalValues?原因太直接了:

  • 原来的OriginalValues已经完全过时无效了,它对应的是你读取数据那一刻的状态,而现在数据库里的状态已经被其他操作修改了,这个旧基准已经不能用来做有效的并发检测了。
  • 如果不更新OriginalValues,你重试SaveChanges()的时候,EF还是会用旧的基准去匹配数据库,依然会触发DbUpdateConcurrencyException,冲突根本解决不了。
  • 当然,这一步只是解决了「让更新操作能重新发起」的基础问题,通常你还需要结合业务逻辑:比如把用户当前的修改(CurrentValues)合并到最新的数据库值上,或者提示用户确认是否覆盖,再执行后续的保存操作。

举个接地气的例子:

你读取了一条商品记录,价格是100(OriginalValues=100),然后你想改成120。这时候另一个操作把价格改成了110,你调用SaveChanges()时抛出并发异常。
执行这段代码后,OriginalValues被改成了110,然后你可以把CurrentValues的价格改成120(或者根据业务做合并),再调用SaveChanges()——这时候EF会用WHERE 价格=110去匹配,只要没人再改,就能成功更新到120。

内容的提问来源于stack exchange,提问作者HTTP 418

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:39:31