EF Core 6常规更新方法未按预期触发RowVersion并发校验问题
核心原因
这个现象本质是EF Core跟踪机制对并发令牌的处理逻辑导致的:
- 默认跟踪查询得到的实体,会被EF Core在上下文中留存一份原始值快照,生成UPDATE语句时,所有并发校验逻辑(包括RowVersion校验)使用的都是快照里的原始值,而非你后续给实体属性手动赋的新值。
你在跟踪场景下直接给p.RowVersion赋值,仅修改了该属性的当前值,既不会覆盖上下文保存的原始快照值,也不会让EF把你赋的值作为校验依据——最终生成的SQL语句WHERE条件里携带的还是查询时拿到的真实RowVersion,自然不会触发并发异常。 - 用
AsNoTracking查询的实体不会被上下文跟踪,EF Core没有留存它的原始值快照。当你调用db.Update()方法时,EF Core会将实体所有属性标记为已修改,同时直接使用当前实体上携带的属性值作为并发校验的原始值拼入WHERE条件。这时候你手动赋值的错误RowVersion会被用于校验,和数据库实际存储值不匹配时就会正常抛出DbUpdateConcurrencyException。
正确实现方式(无需调用Update,支持仅更新变更字段)
db.Update()不是触发RowVersion校验的唯一方式,你只需要在跟踪场景下,显式将客户端传回的RowVersion设置为属性的原始值,就能在保留「仅更新变更字段」特性的同时正常触发并发校验,示例代码如下:
using (PeopleContext db = new()) { // 常规跟踪查询 Person p = await db.People.FirstAsync(x => x.Id == guid); // 仅修改需要更新的属性 p.Name = "FE"; // 关键操作:将客户端提交的RowVersion设置为并发校验用的原始值 db.Entry(p).Property(x => x.RowVersion).OriginalValue = Convert.FromBase64String("AAAAAADDC9I="); // 保存时会自动用你设置的原始RowVersion做并发校验,且仅生成修改字段的UPDATE语句 await db.SaveChangesAsync(); }
注意:不要直接给实体的RowVersion属性赋值,RowVersion是SQL Server自动生成维护的计算列,业务代码不应该修改它的当前值,仅需要在做并发校验时将客户端回传的旧值设置为原始值即可。
内容的提问来源于stack exchange,提问作者hablahat
相关产品推荐
相关产品推荐

