默认事务(SaveChanges)
- SaveChanges :一次性将本地缓存中所有的状态变化,一次性提交到数据库,这就是一个事务,要么统一成功,要么统一回滚
- 使用场景:一个DBContext,即一个数据库的EF的上下文,不能控制多个数据库。
关闭默认事务:db_a.Database.AutoTransactionsEnabled = false ; //这个新版本不生效了/// <summary> /// SaveChanges :一次性将本地缓存中所有的状态变化一次性提交到数据库,这就是一个事务,要么统一成功,要么统一回滚 /// 使用场景:一个DBContext,即一个数据库的EF的上下文,不能控制多个数据库。 /// </summary> private string DoTrans_SaveChanges() { try { using (A_DbContext db_a = new A_DbContext()) { //关闭SaveChanges的默认事务--这个新版本不生效了 db_a.Database.AutoTransactionsEnabled = false; var gid = Guid.NewGuid().ToString("N"); //插入一条正确的 db_a.Add(new UserInfo() { Name = "张三_A", G_Id = gid,//唯一键 Money = 100, }); //插入一条错误的 (唯一键报错) db_a.Add(new UserInfo() { Name = "张三_B", G_Id = gid,//唯一键 Guid.NewGuid().ToString("N"),// Money = 99, }); ; var row = db_a.SaveChanges(); return "保存成功,影响行数=" + row; } } catch (Exception ex) { return "出错了两条数据都没有执行成功:" + ex.Message; } }
DbContextTransaction
- 通常用于手动接管事务,某些操作是一个事务,某些操作是另外一个事务。
- 使用场景:EF调用SQL语句的时候使用该事务、 多个SaveChanges;不同控制多个数据库
- 使用方式:BeginTransaction开启事务、Commit提交事务、Rollback回滚事务、Dispose销毁,如果用Using包裹的话,不再需要手动Rollback,走完Using会自动回滚。如果不用Using包裹事务,就需要在Catch中手动RollBack回滚,并且最好最后手动的Dispose一下
private string DoTrans_DbContextTransaction() { using A_DbContext db = new(); var transaction = db.Database.BeginTransaction(); try { var userEntity = new UserInfo() { Name = "张三_B", G_Id = Guid.NewGuid().ToString("N"), Money = 99, }; db.Add(userEntity); var row_1 = db.SaveChanges(); db.Add(new Product() { Name = "产品_1", UserInfo_Id = userEntity.Id }); var row_2 = db.SaveChanges(); transaction.Commit(); return $"保存成功,UserInfo={row_1},Product={row_2}"; } catch (Exception ex) { transaction.Rollback(); return "出错了:" + ex.ToString(); } finally { transaction.Dispose(); } }
TransactionScope-环境事务
- 使用场景
1、同一个上下文的事务。(多个SaveChanges(自增主键后续用到的情况)、SaveChanges和EF调用SQL语句混用)
2、多种数据库技术,访问同一个数据库的事务
3、同一个数据库,多个不同的上下文是支持的
4、不同数据库的上下文是不支持的,目前Core平台下不支持分布式事务 - 使用方式
new TransactionScope创建事务、Complete提交事务、 Transaction.Current.Rollback();回滚事务、Dispose销毁对象。如果用Using包裹的话,不再需要手动Rollback,走完Using会自动回滚。如果不用Using包裹事务,就需要在Catch中手动RollBack回滚,并且最好最后手动的Dispose一下。private string DoTrans_TransactionScope() { using (var scope = new TransactionScope()) { try { //A数据库 using A_DbContext db_a = new(); var userEntity = new UserInfo() { Name = "张三_A", G_Id = Guid.NewGuid().ToString("N"), Money = 99, }; db_a.Add(userEntity); var row_1 = db_a.SaveChanges(); //A数据库 using A2_DbContext db_a2 = new(); db_a2.Add(new Product() { Name = "产品_1", UserInfo_Id = userEntity.Id }); var row_2 = db_a2.SaveChanges(); //综合提交 scope.Complete(); return $"保存成功,UserInfo={row_1},Product={row_2}"; } catch (Exception ex) { return "出错了:" + ex.ToString(); } } }
System.Transactions 的限制,不同数据库的上下文是不支持的,目前Core平台下不支持分布式事务
- 开启msdtc服务的步骤: cmd命令→ net start msdtc ,然后发现报错:This platform does not support distributed transactions.说明目前Core平台下不支持分布式事务
-
EF Core 依赖数据库提供程序以实现对 System.Transactions 的支持。 如果提供程序未实现对 System.Transactions 的支持,则可能会完全忽略对这些 API 的调用。 SqlClient 支持它。
-
自 .NET Core 2.1 起,System.Transactions 的 .NET Core 实现当前不包括对分布式事务的支持,因此不能使用
TransactionScope
或CommittableTransaction
来跨多个资源管理器协调事务。 -
参考资料:https://learn.microsoft.com/zh-cn/ef/core/saving/transactions
/// <summary> /// 不支持 的分布式事务 /// </summary> /// <returns></returns> private string DoTrans_TransactionScope_NoSupport() { using (var scope = new TransactionScope()) { try { //A数据库 using A_DbContext db_a = new(); var userEntity = new UserInfo() { Name = "张三_A", G_Id = Guid.NewGuid().ToString("N"), Money = 99, }; db_a.Add(userEntity); var row_1 = db_a.SaveChanges(); //B数据库 using B_DbContext db_b = new(); db_b.Add(new Product() { Name = "产品_B", UserInfo_Id = userEntity.Id }); var row_2 = db_b.SaveChanges(); //综合提交 scope.Complete(); return $"保存成功,UserInfo={row_1},Product={row_2}"; } catch (Exception ex) { return "出错了:" + ex.ToString(); } } }