【并发控制、更新、版本控制】.NET开源ORM框架 SqlSugar 系列

系列文章目录

🎀🎀🎀 .NET开源 ORM 框架 SqlSugar 系列 🎀🎀🎀


文章目录

  • 系列文章目录
  • 一、并发累计(累加)
    • [1.1 单条批量累计+](#1.1 单条批量累计+)
    • [1.2 批量更新并且字段+1](#1.2 批量更新并且字段+1)
    • [1.3 批量更新并且字段+list中对应的值](#1.3 批量更新并且字段+list中对应的值)
  • 二、防止提交覆盖,重复提交(乐观锁)
    • [2.1 不依赖库同步 (新功能)](#2.1 不依赖库同步 (新功能))
    • [2.2 依赖库同步(老功能)](#2.2 依赖库同步(老功能))
    • [2.3 一对多提交](#2.3 一对多提交)
  • 三、悲观锁的用法
    • [3.1 悲观锁等待模式](#3.1 悲观锁等待模式)
    • [3.2 悲观锁排它模式](#3.2 悲观锁排它模式)
    • 在这里插入图片描述
  • [🎀🎀🎀 .NET开源 ORM 框架 SqlSugar 系列 🎀🎀🎀](#🎀🎀🎀 .NET开源 ORM 框架 SqlSugar 系列 🎀🎀🎀)

一、并发累计(累加)

1.1 单条批量累计+

比如要扣钱什么的,或者数字叠加,这种就需要通过 set 字段=段字段+1 这种方处理。

csharp 复制代码
//正确写法:安全的字段累计
var result= db.Updateable<Student>().SetColumns(it => it.Num== it.Num+1).Where(it => it.Id == 1).ExecuteCommand();
//sql: num=num+1 
 
 
//错误写法: 在程序中计算是不安全的
var num=data.num+1
var result= db.Updateable<Student>().SetColumns(it => it.Num== num).Where(it => it.Id == 1).ExecuteCommand();
//sql:  num=值

🎯注意:这种更新方式只适合明确主键的更新,条件形态的建议用下面的锁。

1.2 批量更新并且字段+1

csharp 复制代码
 var result67 =
          db.Updateable(updateObjs)
          //批量更新单独处理num列 set num=num+1
          .PublicSetColumns(it => it.Num, it => it.Num+ 1)
          .ExecuteCommand();

1.3 批量更新并且字段+list中对应的值

csharp 复制代码
   db.Updateable(list)
   PublicSetColumns(it => it.Price, "+") //set price=price+list[i].price
   .ExecuteCommand();

二、防止提交覆盖,重复提交(乐观锁)

📌使用乐观锁需要满足2个条件:

  1. 用户要打开编辑界面,然将数据绑定到编辑界面
  2. 用户在界面填写完在点修改保存

💯原理: 打开编辑框太久别人已经修改了这条记录,我在提交就可能把别人更新数据清空掉,我们需要一个时间字段去和数据库中的Version比对

🎇重现步骤:开2个浏览器 ,编辑同一条记录, A浏览器先保存,然后B浏览器在保存就能重现

因为是同一条记录本身并不存在并发,因为编辑界面打开一天也不会刷新数据,点保存一样覆盖

2.1 不依赖库同步 (新功能)

新项目推荐用新功能,使用方便有问题及时沟通

csharp 复制代码
    public class ULockEntity
    {
        [SqlSugar.SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        public string Name { get; set; }
        [SqlSugar.SugarColumn(IsEnableUpdateVersionValidation = true)]//标识版本字段
        public long Ver { get; set; } 
         
        //支持Guid long string DateTime (不推荐DateTime 时间有精度问题)
        //推荐用 string guid  (使用long要序列化成strting不然前端精度丢失)
    }
     
    //注意:只能是实体更新不能是集合更新
     
    //【用法1:不扔错】 Ver与数据库字段不同不报错返回0
    var rows = db.Updateable(new ULockEntity()
    {
        Id = id,
        Name = "newname",
        Ver = 1551128313597136896//版本字段会自动更新
    }).ExecuteCommandWithOptLock(); 
     
     
    //【用法2:扔出错误】Ver与数据库字段不同直接扔错出误
    var rows = db.Updateable(new ULockEntity()
    {
        Id = id,
        Name = "newname",
        Ver = 1551128313597136896  //版本字段会自动更新
    }).ExecuteCommandWithOptLock(true); //加上true就会扔出错误
    //try { ... }catch(VersionExceptions ex){...}
    //底层 throw new VersionExceptions
    
    /*********完整测试用例*********/
     
    //添加测试数据
    var db = NewUnitTest.Db;
    db.CodeFirst.InitTables<ULockEntity>();
    db.DbMaintenance.TruncateTable<ULockEntity>();
    //第一次插入ver=0
    var id = db.Insertable(new ULockEntity(){Name = "oldName" ,Ver=0}).ExecuteReturnIdentity();
     
    //开始用例
    var rows= db.Updateable(new ULockEntity()
    {
        Id = id,
        Name = "newname",
        Ver = 0  //会自动更新版本字段更新后数据库将不在是0
    }).ExecuteCommandWithOptLock();  
    //rows=1 因为数据库ver是0你传的也是0
 
    var rows= db.Updateable(new ULockEntity()
    {
        Id = id,
        Name = "newname2",
        Ver = 0
    }).ExecuteCommandWithOptLock();   
    //rows=0  失败:数据库ver不等于0

2.2 依赖库同步(老功能)

例如:SqlServer 中的时间戳类型的字段,会在这条记录变更后会自动更新,如果没这种机质的字段你也可以用触发器实现这种机质

有这种机质我们只要打个特性就能实现并发控制,代码如下:

数据库 数据库 实体
SQLSERVER timestamp默认自动更新) byte[]
其他数据库 timestamp(需要配置自动更新) DateTime
csharp 复制代码
public class StudentVersion    
{
  [SugarColumn(IsPrimaryKey =true,IsIdentity =true)]    
 public int Id { get; set; }    
 public string Name { get; set; }    
 public DateTime CreateTime { get; set; }    
 [SqlSugar.SugarColumn(
             IsEnableUpdateVersionValidation = true,//标识版本字段 
             IsOnlyIgnoreInsert=true,//禁止插入
             IsOnlyIgnoreUpdate=true,//禁止更新
             ColumnDataType="timestamp" //时间戳类型,有些库需要配置或者有差异
              )]   
  
 // SqServer byte[] 其它数据库用 DateTime                        
 public byte[]  Timestamp { get; set; } 
}    
db.Updateable(data).IsEnableUpdateVersionValidation().ExecuteCommand();//更新的时候启用验证

原理:

csharp 复制代码
 //查询这条记录
 var data = db.Queryable<StudentVersion>().InSingle(id);
  
 //成功 (data.Timestamp等于数据库中的 Timestamp字段)
 db.Updateable(data).IsEnableUpdateVersionValidation().ExecuteCommand();//执行后数据库Timestamp更新
  
 //失败 (data.Timestamp不等于数据库中的 Timestamp字段)
 db.Updateable(data).IsEnableUpdateVersionValidation().ExecuteCommand();

如何知道是并发错误,还是其他错误

csharp 复制代码
//底层代码 
throw new VersionExceptions //捕获异常的时候只要是 VersionExceptions 那就是并发错误

2.3 一对多提交

只处理主表数据就行了,从表不需要考虑,因为是一起提交。

三、悲观锁的用法

悲观锁适合用于 读取和插入在同一个步骤下的操作,并且禁止2个请求同时操作

3.1 悲观锁等待模式

🎯定义:2个相同的业务代码 同时执行时必须等待 第一个执行成功后执行。

主键查询一般是行锁,如果非主键可以会变成表锁。

csharp 复制代码
db.BeginTran();
//查询条件记录后锁表
var data=db.Queryable<Order>().TranLock(DbLockType.Wait).Where(it=>it.Id==1).ToList();//返回条数尽量最少尽量主键
//插入、更新等操作
.......
db.CommitTran();
 
//异常要加上
db.RollBackTran();

3.2 悲观锁排它模式

🎯定义:2个相同的业务代码 同时执行时只会生效一个,其它都扔出错误。

csharp 复制代码
db.BeginTran();
//查询条件记录后锁表
var data = db.Queryable<Order>().TranLock(DbLockType.Error).Where(it=>it.Name=="a").ToList();//返回条数尽量最少 for update UPDLOCK
//插入、更新等操作 
.......
db.CommitTran();
 
//异常要加上
db.RollBackTran();

测试代码:

用的来验证是否扔出异常

csharp 复制代码
for (int i = 0; i < 10; i++)
{
    Task.Run(() =>
    {
            var db = GetInstance();//用的sqlsugarclient保证db线程安全每次New一下
        try
        {
          
            db.BeginTran();
            var getAll = db.Queryable<Order>().TranLock(DbLockType.Error).ToList();
            System.Threading.Thread.Sleep(1000);
            db.CommitTran();
            Console.WriteLine("成功");
        }
        catch (Exception ex)
        {
            db.RollBackTran();
            Console.WriteLine("失败");
        }
    });
}

🎀🎀🎀 .NET开源 ORM 框架 SqlSugar 系列 🎀🎀🎀

【开篇】.NET开源 ORM 框架 SqlSugar 系列
【入门必看】.NET开源 ORM 框架 SqlSugar 系列
【实体配置】.NET开源 ORM 框架 SqlSugar 系列
【Db First】.NET开源 ORM 框架 SqlSugar 系列
【Code First】.NET开源 ORM 框架 SqlSugar 系列
【数据事务】.NET开源 ORM 框架 SqlSugar 系列
【连接池】.NET开源 ORM 框架 SqlSugar 系列
【查询目录】.NET开源 ORM 框架 SqlSugar 系列
【查询基础】.NET开源 ORM 框架 SqlSugar 系列
【排序用法】.NET开源 ORM 框架 SqlSugar 系列
【分组去重】.NET开源 ORM 框架 SqlSugar 系列
【联表查询】.NET开源 ORM 框架 SqlSugar 系列
【导航查询】.NET开源 ORM 框架 SqlSugar 系列
【子查询】.NET开源 ORM 框架 SqlSugar 系列
【嵌套查询】.NET开源 ORM 框架 SqlSugar 系列
【配置查询】.NET开源 ORM 框架 SqlSugar 系列
【并集查询】.NET开源 ORM 框架 SqlSugar 系列
【树型查询】.NET开源 ORM 框架 SqlSugar 系列
【表格查询】.NET开源 ORM 框架 SqlSugar 系列
【动态表达式】.NET开源 ORM 框架 SqlSugar 系列
【查询函数】.NET开源ORM框架 SqlSugar 系列
【过滤器】.NET开源 ORM 框架 SqlSugar 系列
【跨库查询、多库查询】.NET开源 ORM 框架
​【报表查询】.NET开源ORM框架 SqlSugar 系列
【Where语法全解密】.NET开源ORM框架 SqlSugar 系列
【Select 语法全解密】.NET开源ORM框架 SqlSugar 系列
【查询返回结果类型】.NET开源ORM框架 SqlSugar 系列
【insert 插入数据语法合集】.NET开源ORM框架 SqlSugar 系列
【SqlSugar雪花ID常见问题】.NET开源ORM框架 SqlSugar 系列
【update 更新数据语法合集】.NET开源ORM框架 SqlSugar 系列


相关推荐
pursue.dreams41 分钟前
马斯克开源X推荐算法深度解析:Grok驱动的推荐系统架构
系统架构·开源·推荐算法·x
OpenCSG1 小时前
KAIYUAN-2B 开源大模型突破解析:OpenCSG 数据集如何破解中文大模型训练困境
开源
昇腾CANN2 小时前
TileLang Ascend:让AI算子开发更简单!
开源·cann
lywybo2 小时前
【开源】赛博报社技术剖析:如何零成本使用AI大模型
人工智能·开源
云草桑2 小时前
.net AI开发05 第九章 新增 RAG 文档处理后台服务 RagWorker 及核心流程
人工智能·ai·.net·rag·qdrant
兆龙电子单片机设计2 小时前
【STM32项目开源】STM32单片机智能宠物管家
stm32·单片机·物联网·开源·毕业设计·宠物
Goway_Hui3 小时前
【开源鸿蒙跨平台开发--KuiklyUI--02】华为云真机部署实战指南
华为·开源·华为云·harmonyos·kuikly
DolphinScheduler社区3 小时前
Linux 环境下,Apache DolphinScheduler 如何驱动 Flink 消费 Kafka 数据?
linux·flink·kafka·开源·apache·海豚调度·大数据工作流调度
时光慢煮3 小时前
构建跨端提示体验:Flutter × OpenHarmony 实现底部 SnackBar 卡片
flutter·华为·开源·openharmony
DolphinScheduler社区3 小时前
深度探秘 Apache DolphinScheduler 数据库模式
数据库·开源·apache·开源社区·海豚调度·大数据工作流调度