如何在 EF Core 中使用乐观并发控制

什么是乐观并发控制?
乐观并发控制是一种处理并发访问的数据的方法,它基于一种乐观的假设,即认为并发访问的数据冲突的概率很低。在乐观并发控制中,系统不会立即对并发访问的数据进行加锁,而是在数据被修改时,再检查是否有其他并发操作已经修改了数据。如果检测到冲突,系统 再采取相应的措施来解决冲突。
EF Core 内置了使用并发令牌列实现的乐观并发控制,所谓的并发令牌列通常就是被并发操作影响的列。请看本文是如何在 EF Core 中使用乐观并发控制的……
使用步骤
创建一个 Asp.net console 项目,并从 Nuget 引用 EF 相关的包
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools配置并发冲突列
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; class HouseConfig : IEntityTypeConfiguration<House>
{
public void Configure(EntityTypeBuilder<House> builder)
{
builder.ToTable("T_Houses");
builder.Property(p => p.Name).IsUnicode().IsRequired();
// 把 Owner 列配置为并发令牌
builder.Property(p => p.Owner).IsConcurrencyToken();
}
}
在 Program.cs 编写以下代码:
using Microsoft.EntityFrameworkCore; Console.WriteLine("请输入您的姓名");
string name = Console.ReadLine()!;
using TestDbContext ctx = new TestDbContext(); // 1.获取数据
var h1 = await ctx.Houses.SingleAsync(h => h.Id == 1);
if (string.IsNullOrEmpty(h1.Owner))
{
// 2.延迟5秒,方便测试
await Task.Delay(5000); // 3.更新数据
h1.Owner = name;
try
{
await ctx.SaveChangesAsync();
Console.WriteLine("抢到手了");
}
catch(DbUpdateConcurrencyException ex)
{
// 4. 捕捉和处理并发冲突
var entry = ex.Entries.First();
var dbValues = await entry.GetDatabaseValuesAsync();
string newOwner = dbValues.GetValue<string>(nameof(House.Owner));
Console.WriteLine($"并发冲突,被{newOwner}提前抢走了");
}
}
// 5.处理数据已存在情况
else
{
if (h1.Owner == name)
{
Console.WriteLine("这个房子已经是你的了,不用抢");
}
else
{
Console.WriteLine($"这个房子已经被{h1.Owner}抢走了");
}
}
Console.ReadLine();
测试
- 清理 T_Houses 表数据,让 Owner 列等于 null
- 同时运行两个控制台程序
- 在第一个控制台程序输入 Tom 并运行
- 在第二个控制台程序输入 Jim 并运行
- 第一个控制台返回消息:抢到手了
- 第二个控制台则返回消息:并发冲突,被Tom提前抢走了
扩展
通常可以通过把并发修改的属性设置为并发令牌的方式启用乐观并发控制。
有时候无法确定到底哪个属性适合作为并发令牌,比如程序在不同的情况下会更新不同的列或者程序会更新多个列,在这种情况下,可以使用设置一个额外的并发令牌属性的方式来使用乐观并发控制。
如果使用Microsoft SQL Server数据库,可以用一个byte[]类型的属性作为并发令牌属性,然后使用IsRowVersion把这个属性设置为RowVersion类型,这个属性对应的数据库列就会被设置为ROWVERSION类型。对于ROWVERSION类型的列,在每次插入或更新行时,Microsoft SQL Server会自动为这一行的ROWVERSION类型的列生成新值。
1. 增加一个额外的byte[]类型的属性
class House
{
public long Id { get; set; }
public string Name { get; set; }
public string? Owner { get; set; }
public byte[] RowVer { get; set; }
} 2. 配置并发令牌
builder.ToTable("T_Houses");
builder.Property(h => h.Name).IsUnicode();
builder.Property(h => h.RowVer).IsRowVersion(); 3. Update 语句中的 Where 中使用 RowVer 列
其它数据库也可以使用 Guid 作为并发令牌控制
乐观并发控制能够避免悲观锁带来的性能下降、死锁等问题,推荐使用乐观并发控制而不是悲观锁
如何在 EF Core 中使用乐观并发控制的更多相关文章
- 文章翻译:ABP如何在EF core中添加数据过滤器
原文地址:https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-data-filter-ef-cor ...
- EF Core中的多对多映射如何实现?
EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的.但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考<你必须掌握的Entity ...
- 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)
原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...
- 如何在EF Core 使用存储过程
使用EF Core框架能快速的帮助我们进行常规的数据处理和项目开发,但是ORM虽然好用,但是在许多复杂逻辑的数据处理时,我个人还是偏向用SQL和存储过程的方式去处理,但是研究了一下目前最新版本的EF ...
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
- [小技巧]EF Core中如何获取上下文中操作过的实体
原文地址:https://www.cnblogs.com/lwqlun/p/10576443.html 作者:Lamond Lu 源代码:https://github.com/lamondlu/EFC ...
- EF Core中避免贫血模型的三种行之有效的方法(翻译)
Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...
- EF Core中执行Sql语句查询操作之FromSql,ExecuteSqlCommand,SqlQuery
一.目前EF Core的版本为V2.1 相比较EF Core v1.0 目前已经增加了不少功能. EF Core除了常用的增删改模型操作,Sql语句在不少项目中是不能避免的. 在EF Core中上下文 ...
- EF Core中DbContext可以被Dispose多次
我们知道,在EF Core中DbContext用完后要记得调用Dispose方法释放资源.但是其实DbContext可以多次调用Dispose方法,虽然只有第一次Dispose会起作用,但是DbCon ...
- 9.4 翻译系列:EF 6以及 EF Core中的NotMapped特性(EF 6 Code-First系列)
原文链接:http://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-f ...
随机推荐
- DM数据库 回表优化案例
京华开发一哥们找我优化条SQL,反馈在DM数据库执行时间很慢需要 40s 才能出结果,安排. 原SQL: SELECT A.IND_CODE, A.IND_NAME AS "specialN ...
- 8.0的新特性 -- invisible(不可见索引)
版权声明:原创作品,谢绝转载!否则将追究法律责任. ----- 作者:kirin invisible index 不可见索引 以前的版本,假如这个索引无用了会怎么样?那就只能是drop掉了.但是dro ...
- MySQL-防止误删除的方案就是删除,看不见岂不就是删除了吗,所以就是把它隐藏起来。
版权声明:原创作品,谢绝转载!否则将追究法律责任. ----- 作者:kirin 伪删除: 用update替代delete 1.添加状态列 ALTER TABLE student2 ADD state ...
- MySQL-管理员root@'locahost' 丢失,怎么处理?
版权声明:原创作品,谢绝转载!否则将追究法律责任. ----- 作者:kirin 跳过授权表 ----> 不开启验证功能(无密码登录) --skip-grant-tables 阻止所有tcp/i ...
- Go切片是值传递还是引用传递?
Go没有引用传递和引用类型!!! 很多人有个误区,认为涉及Go切片的参数是引用传递,或者经常听到Go切片是引用类型这种说法,今天我们就来说一下方面的问题. 什么是值传递? 将实参的值传递给形参,形参是 ...
- 记一次 .NET 某零售管理系统 存储不足分析
一:背景 1. 讲故事 前几天有位朋友找到我,说他的程序会偶发性的报 存储空间不足,无法处理此命令 的错误,让我帮忙看下到底怎么回事,哈哈,人家是有备而来,dump都准备好了,话不多说,直接分析开干. ...
- MVC控制器传DataTable
MVC中,控制器return Json(DataTable)会出现错误: 序列化类型为"System.Reflection.RuntimeModule"的对象时检测到循环引用. 解 ...
- linux笔记一(基础命令)
总结: 1.ls – List ls会列举出当前工作目录的内容(文件或文件夹),就跟你在GUI中打开一个文件夹去看里面的内容一样. 2.mkdir – Make Directory mkdir 用 ...
- java-导出pdf
前言: 纯代码画pdf格式 <!-- iText PDF --> <dependency> <groupId>com.itextpdf</groupId& ...
- LeetCode224:基本计算器(栈)
解题思路: 1.双栈模拟,一个用来存数,一个用来存操作符.需要考虑 '('后面紧跟'+'.'-'这种情况 2.递归:遇到左括号开始递归,遇到右括号结束递归,返回值. 1 class Solution: ...