一、什么是TransactionScope?
TransactionScope即范围事务(类似数据库中的事务),保证事务声明范围内的一切数据修改操作状态一致性,要么全部成功,要么全部失败回滚.
MSDN:如果在事务范围内未不发生任何异常 (即之间的初始化 TransactionScope 对象并调用其 Dispose 方法),则范围所参与的事务可以继续,否则参与到其中的事务将回滚。
当应用程序完成所有工作时它想要在事务中执行,应调用 Complete 方法一次,以通知该事务管理器是可接受(此时事务并未提交),即可提交事务,未能调用此方法中止事务。
调用 Dispose 方法将标记事务范围的末尾。 在调用此方法之后所发生的异常不会影响事务。
二、TransactionScope有什么用?
假如现在有一个需求:实现一个下单功能,主要业务包含扣减商品库存、扣减用户账户余额、生成订单记录以及记录日志。为了保证数据一致性我们通常的做法就是在数据库建一个下订单的事务,然后程序调用事务传入相应的参数即可。那么问题来了,如果用户的账户数据跟订单数据分别处于不同的数据库,就没法在同一个数据库事务里完成所有任务,也就没法保证数据的一致性。
最近由于业务的变更公司改用MySQL数据库,处理数据变更时习惯性先写事务,写的时候发现现有数据库中一个事务都没有,于是去问java组,不使用事务怎么保证数据的一致性?得到的答复是:事务是什么鬼,spring帮我们解决所有问题...。立马就懵逼了,.net中没听说有Spring啊(据说有类似的框架),虽然可以考虑使用仓储加工作单元来解决,但是感觉好麻烦的样子,后来寻找解决方案时发现了TransactionScope。
三、TransactionScope怎么使用?
1 try 2 { 3 using (TransactionScope scope = new TransactionScope()) 4 { 5 //TODO:数据处理业务 6 scope.Complete(); 7 } 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 }
四、问题探讨
1、准备工作
1 //数据库访问对象 2 public class Studuent 3 { 4 public static IList<StudentModel> GetList() 5 { 6 //不允许脏读 7 string sql = "SELECT Id,Name,CreateTime FROM Student ORDER BY Id DESC;"; 8 DataTable dt = SQLHelper.ExecuteDataTable(CommandType.Text, sql); 9 return dt.ToModel<StudentModel>(); 10 } 11 12 public static bool Add(string name) 13 { 14 SqlParameter[] parms = { new SqlParameter("@Name", SqlDbType.NVarChar, 32) { Value = name } }; 15 string sql = "INSERT INTO Student (Name) VALUES (@Name)"; 16 return SQLHelper.ExecuteNonQuery(CommandType.Text, sql, parms) > 0; 17 } 18 19 public static void Clear() 20 { 21 SQLHelper.ExecuteNonQuery("DELETE Student"); 22 } 23 }
1 //公共方法,输出学生列表 2 static void PrintStudent() 3 { 4 IList<StudentModel> list = Studuent.GetList(); 5 foreach (var item in list) 6 { 7 Console.WriteLine("{0} {1} {2}", Thread.CurrentThread.ManagedThreadId, item.Name, item.CreateTime); 8 } 9 Console.WriteLine(); 10 }
2、事务什么时候提交?
最初我以为在执行Complete后即刻提交,但根据输出结果可以看出,Complete方法执行两秒之后事务依旧没有提交。因为不允许脏读的原因,主线程会在事务对Student表操作完成后才可查询完成,但学生列表是在scope调用dispose方法之后输出,MSDN介绍说dispose确定事务范围末尾,因此猜测事务是在调用dispose时被提交。
3、异常是怎么导致数据不被提交?
对比两幅图可以看出,异常在Complete之后发生并不会影响事务的提交,事务未提交是因为发生异常导致Complete未被执行。之前看过一篇文章说,如果TransactionScope范围中没有调用Complete会导致程序异常,我想他肯定是开玩笑的...