首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
SAAS
ToB门户
了解全球最新的ToB事件
论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
微博
Follow
记录
Doing
博客
Blog
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
排行榜
Ranklist
相册
Album
应用中心
qidao123.com ToB IT社区-企服评测·应用市场
»
论坛
›
物联网/工业互联网
›
物联网
›
EF Core 乐观并发控制(并发令牌)
返回列表
发新帖
EF Core 乐观并发控制(并发令牌)
[复制链接]
发表于 2025-3-29 15:38:59
|
显示全部楼层
|
阅读模式
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要
登录
才可以下载或查看,没有账号?
立即注册
×
前言
Entity Framework (EF) Core 默认支持 乐观并发控制(Optimistic Concurrency Control),它通过检测数据冲突(而不是显式加锁)来保证数据一致性。
一、乐观并发的核心头脑
无锁机制
:允许多个事务同时读取和修改数据,提交时检查数据是否被其他事务修改。
冲突检测
:通过
版本
号(
RowVersion
)或字段值比较,如果数据已被修改,则抛出
DbUpdateConcurrencyException
。
二、实现方法
1)使用并发令牌(Concurrency Token)
为实体添加一个并发标记字段(如
RowVersion
),每次更新时检查该字段是否与
数据库
中的值一致。
示例
:通过 [
ConcurrencyCheck
] 特性标记字段
public class House
{
public long Id { get; set; }
public string Name{ get; set; }
[ConcurrencyCheck] // 标记为并发令牌
public string Owner{ get; set; }
}
复制
代码
示例
:或通过
Fluent
API
配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<House>()
.Property(p => p.Owner)
//.IsRowVersion() // 自动映射为 SQL Server 的 `rowversion` 类型
.IsConcurrencyToken(); // 标记为并发令牌
}
复制
代码
public class House
{
public long Id { get; set; }
public string Name{ get; set; }
public string Owner{ get; set; }
public byte[] RowVersion { get; set; }
}
public void Configure(EntityTypeBuilder<House> builder)
{
builder.ToTable("T_Houses");
builder.Property(h=>h.Name).IsRequired();
//builder.Property(h=>h.Owner).IsConcurrencyToken();
builder.Property(h=>h.RowVersion).IsConcurrencyToken().IsRowVersion();
}
复制
代码
2)处置惩罚并发冲突
当检测到数据已被修改时,EF Core 会抛出 DbUpdateConcurrencyException,开发者需捕获并处置惩罚冲突。
try
{
var house = await context.Houses.FindAsync(houseId);
house.Owner = "Tom";
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
// 处理冲突
var entry = ex.Entries.Single();
var databaseValues = await entry.GetDatabaseValuesAsync();
if (databaseValues == null)
{
// 数据已被删除
Console.WriteLine("数据已被删除!");
}
else
{
// 获取当前
数据库
中的值
var currentValues = databaseValues.ToObject() as House;
// 策略1:使用
数据库
最新值覆盖当前修改
entry.OriginalValues.SetValues(databaseValues);
// 策略2:合并值(手动处理冲突)
// currentValues 是数据库中的最新值
// entry.Entity 是当前尝试提交的值
// 例如:保留用户修改的某些字段,合并其他字段
entry.Entity.Owner= "Tom";
entry.Entity.RowVersion = currentValues.RowVersion;
// 重新提交
await context.SaveChangesAsync();
}
}
复制代码
三、工作原理
查询数据
:读取数据时,
EF Core
会记录并发令牌的原始值(如
RowVersion
)。
更新数据
:提交修改时,生成的
SQL
会包罗
WHERE
条件,检查并发令牌是否未被修改。
UPDATE [T_Houses]
SET [Owner] = @p0
WHERE [Id] = @p1 AND [Owner] = @p2;
复制代码
冲突检测
:如果受影响的行数为 0(即
RowVersion
不匹配),抛出异常。
四、实用场景
低冲突概率
:得当大部门时间数据竞争较少的场景。
高吞吐需求
:避免锁机制的开销,提拔
性能
。
分布式体系
:无锁机制更得当跨服务的并发操作。
五、与悲观并发的对比
乐观并发悲观并发
实现方式
版本
号或字段检查(RowVersion、并发令牌)显式加锁(事务+锁机制)
性能
低冲突时更高效高竞争时可能更高效
复杂度
EF Core 内置支持,自动检测冲突必要手动管理锁和事务
数据竞争
可能需重试或合并数据逼迫串行化,避免冲突
六、最佳实践
选择并发令牌
优先使用
RowVersion
(自动递增的二进制字段),而非业务字段。
若使用业务字段(如
LastUpdatedTime
),需确保其值在每次更新时被修改。
冲突处置惩罚策略
客户端优先
:逼迫覆盖数据库的值(需谨慎)。
数据库优先
:放弃当前修改,使用最新值。
合并值
:手动合并冲突字段(如用户编辑的字段优先)。
重试机制
在分布式体系中,可为关键操作添加重试逻辑(如
Polly
库)。
总结
EF Core
的乐观并发通过
版本
号或字段值检测冲突,无需显式加锁,得当低竞争场景。通过
ConcurrencyCheck
或
IsRowVersion()
配置
并发令牌,并在冲突时通过
DbUpdateConcurrencyException
实现机动的数据合并或重试逻辑。
乐观并发控制能够避免悲观锁带来的
性能
、死锁等题目,因此推荐使用并发控制而不是悲观锁。
如果有一个确定的字段要被进行并发控制,那么使用IsConcurrencyToken()把这个字段设置为并发令牌即可;
如果无法确定一个唯一的并发令牌列,那么就可以引入一个额外的属性设置为并发令牌,而且在每次更新数据库的时间,手动更新这一列的值;如果用的是SQL Server数据库,那么也可以接纳RowVersion列,设置为并发令牌列。
回复
使用道具
举报
返回列表
浏览过的版块
容器及微服务
SQL-Server
Oracle
移动端开发
DevOps与敏捷开发
Java
SAAS
linux
数据仓库与分析
.Net
慢吞云雾缓吐愁
+ 我要发帖
登录后关闭弹窗
登录参与点评抽奖 加入IT实名职场社区
去登录
微信订阅号
微信服务号
微信客服(加群)
H5
小程序
快速回复
返回顶部
返回列表