FreeSql实现了四种数据库事务的使用方法,脏读等事务相关方法暂时未提供。主要原因系这些方法各大数据库、甚至引擎的事务级别五花八门较难统一。
事务用于处理数据的一致性,处于同一个事务中的操作是一个UnitOfWork,要么全部执行成功,要么全部执行失败。
指定事务对象
FreeSql 提供了指定事务对象的方法,将事务对象暴露给外部;
orm.Update().WithTransaction(指定事务)
.Set(a => a.Clicks + 1).ExecuteAffrows();
ISelect、IInsert、IUpdate、IDelete,都支持 WithTransaction 方法。
同线程事务
假设用户购买了价值100元的商品:
第一步:扣余额;
第二步:扣库存;
第一步成功了,到了第二步发现库存不足时,事务可以回滚,扣余额的数据将不生效。
//假设已经有了其他wiki页的IFreeSql声明
fsql.Transaction(() => {
var affrows = fsql.Update().Set(a => a.Wealth - 100)
.Where(a => a.Wealth >= 100)
//判断别让用户余额扣成负数
.ExecuteAffrows();
if (affrows < 1) {
throw new Exception("用户余额不足");
//抛出异常,事务退出
}
affrows = fsql.Update().Set(a => a.Stock - 1)
.Where(a => a.Stock > 0)
//判断别让用库存扣成负数
.ExecuteAffrows();
if (affrows < 1) {
throw new Exception("商品库存不足");
//抛出异常,回滚事务,事务退出
//用户余额的扣除将不生效
}
//程序执行在此处,说明都扣成功了,事务完成并提交
});
注意与说明:
1、数据库事务在线程挂载,每个线程只可开启一个事务连接,重复开启会获取线程已开启的事务;
2、在事务代码过程中,不可使用异步方法,包括FreeSql提供的数据库异步方法,否则线程将会切换事务不生效;
3、fsql.Transaction 有防止死锁机制,60秒事务未结束的,将会被其他线程强行提交(不是回滚),可能造成不完整的事务,但仔细一想60秒还没完成的事务是什么原因呢?如果嫌60秒太少了可以在重载方法的参数中设置;
后续我们将介绍仓储模式下的工作单元,和 DbContext 事务使用。
系列文章导航
(一)入门
(二)自动迁移实体
(三)实体特性
(四)实体特性 Fluent Api
(五)插入数据
(六)批量插入数据
(七)插入数据时忽略列
(八)插入数据时指定列
(九)删除数据
(十)更新数据
(十一)更新数据 Where
(十二)更新数据时指定列
(十三)更新数据时忽略列
(十四)批量更新数据
(十五)查询数据
(十六)分页查询
(十七)联表查询
(十八)导航属性
(十九)多表查询
(二十)多表查询 WhereCascade
(二十一)查询返回数据
(二十二)Dto 映射查询
(二十三)分组、聚合
(二十四)Linq To Sql 语法使用介绍
(二十五)延时加载
(二十六)贪婪加载 Include、IncludeMany、Dto、ToList
(二十七)将已写好的 SQL 语句,与实体类映射进行二次查询
(二十八)事务
(二十九)Lambda 表达式
(三十)读写分离
(三十一)分区分表
(三十二)Aop
(三十三)CodeFirst 类型映射
(三十四)CodeFirst 迁移说明
(三十五)CodeFirst 自定义特性