EFCore (三)悲观锁 和 乐观锁
原理
UPDATE [Person] SET [FirstName] = @p1
WHERE [PersonId] = @p0 AND [LastName] = @p2;
- 在 SaveChanges 期间捕获 DbUpdateConcurrencyException。
- 使用 DbUpdateConcurrencyException.Entries 为受影响的实体准备一组新更改。
- 刷新并发令牌的原始值以反映数据库中的当前值。
- 重试该过程,直到不发生任何冲突。
乐观并发控制
并发令牌 IsConcurrencyToken() * 单个字段设置并发
经典例子就是两个人同时执行了抢购业务 Update是同时执行的,这就导致后面的会覆盖前面的,导致业务出现问题!
原理就是在 update的表中 Owner = 旧值
1、我们需要 只需要让EFCore在 code first下配置实体 设置一下Owner 字段为 IsConcurrencyToken() 设置为并发令牌
2、例如:“builder.Property(x=>x.Owner).IsConcurrencyToken()”
3、在保存的时候进行 SaveChangesAsync() 添加
catch(DbUpdateConcurrencyException ex) 进行捕获
例如:
try{
await ctx.SaveChangesAsync();
Console.WriteLine("抢到手了");
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.First();
var dbValues = await entry.GetDatabaseValuesAsync();
string newOwner = dbValues.GetValue<string>(nameof(House.Owner));
Console.WriteLine($"并发冲突,被{newOwner}提前抢走了");
}
效果图
测试
生成的SQL
可以看到 我们定义的并发令牌字段 IsConcurrencyToken 会在 Update And 带上这个字段 进行查询
可能比较抽象 这里我们可以用具体的sql 语句进行分析
例如:
第一次执行的 Update T_houses Set Owner= 'tom' where Id =1 And Owner = '' 注意:此时Owner 是没有值的 所以受影响行数为 1
第二次执行的 Update T_houses Set Owner= 'tom' where Id =1 And Owner = ''
注意:此时Owner 第一次执行后Owner已经被赋值了,所以此时 where Id =1 的Owner是有值的,所以where条件是不满足 受影响行数为 0
RowVersion * 多字段设置并发
IsRowVersion()
需要在表中增加 Byte[] 类型的字段 来存储 RowVersion 类型 (限制在SQLServer)在MySQL 中可以使用 GUID ,但是每次update 需要我们手动进行Guid 修改
配置字段的时候 builder.Property(x=>x.RowVer).IsRowVersion()
在执行Update时,会把RowVersion添加到where子句中(就像这样:update tb set cloName=xxx where Id=@id and RowVersion=@rowversion),如果where子句中的RowVersion值和数据库中的不一样就抛出DbUpdateConcurrencyException。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)