事务的困惑

今天在研究petshop的PetShop.SQLProfileDAL.PetShopProfileProvider时有幸看到事务的处理,其实之前在消息队列的时候也有遇到过,

但那时候忙着整理消息队列,故没怎么去想这个,现在再次遇到就一并记录下来吧,老规矩还是先看下SQLProfileDAL.PetShopProfileProvider

里的代码吧:

1 publicvoid SetAccountInfo(int uniqueID, AddressInfo addressInfo) {
2 string sqlDelete ="DELETE FROM Account WHERE UniqueID = @UniqueID;";
3 SqlParameter param =new SqlParameter("@UniqueID", SqlDbType.Int);
4 param.Value = uniqueID;
5
6 string sqlInsert ="INSERT INTO Account (UniqueID, Email, FirstName, LastName, Address1, Address2, City, State, Zip, Country, Phone) VALUES (@UniqueID, @Email, @FirstName, @LastName, @Address1, @Address2, @City, @State, @Zip, @Country, @Phone);";
7
8 SqlParameter[] parms = {
9 new SqlParameter("@UniqueID", SqlDbType.Int),
10 new SqlParameter("@Email", SqlDbType.VarChar, 80),
11 new SqlParameter("@FirstName", SqlDbType.VarChar, 80),
12 new SqlParameter("@LastName", SqlDbType.VarChar, 80),
13 new SqlParameter("@Address1", SqlDbType.VarChar, 80),
14 new SqlParameter("@Address2", SqlDbType.VarChar, 80),
15 new SqlParameter("@City", SqlDbType.VarChar, 80),
16 new SqlParameter("@State", SqlDbType.VarChar, 80),
17 new SqlParameter("@Zip", SqlDbType.VarChar, 80),
18 new SqlParameter("@Country", SqlDbType.VarChar, 80),
19 new SqlParameter("@Phone", SqlDbType.VarChar, 80)};
20
21 parms[0].Value = uniqueID;
22 parms[1].Value = addressInfo.Email;
23 parms[2].Value = addressInfo.FirstName;
24 parms[3].Value = addressInfo.LastName;
25 parms[4].Value = addressInfo.Address1;
26 parms[5].Value = addressInfo.Address2;
27 parms[6].Value = addressInfo.City;
28 parms[7].Value = addressInfo.State;
29 parms[8].Value = addressInfo.Zip;
30 parms[9].Value = addressInfo.Country;
31 parms[10].Value = addressInfo.Phone;
32
33 SqlConnection conn =new SqlConnection(SqlHelper.ConnectionStringProfile);
34 conn.Open();
35 SqlTransaction trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
36
37 try {
38 SqlHelper.ExecuteNonQuery(trans, CommandType.Text, sqlDelete, param);
39 SqlHelper.ExecuteNonQuery(trans, CommandType.Text, sqlInsert, parms);
40 trans.Commit();
41 }
42 catch(Exception e) {
43 trans.Rollback();
44 thrownew ApplicationException(e.Message);
45 }
46 finally {
47 conn.Close();
48 }
49 }

其实这是一个Delete和Insert的操作,但都是插入到Account表,但这两个对数据库的操作应该都算是w(修改)操作吧,

我在MSDN中查了IsolationLevel这个枚举,ReadCommitted的解释是:在正在读取数据时保持共享锁,以避免脏读,

但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。

如果用ReadCommitted导致不可重复读的话,那么就是说,T1、T2在是并发执行的,但没有加锁,从而导致T1两次(或多次)

读取的数据不一致。但,上面的解释又说避免了脏读,所以呢,如果T1发生错误回滚的话,T2读取的数据不会不一致。

但是,用ReadCommitted读取数据时保持共享锁根本就不会发生脏读的,根据共享税的规定,其他事务也只能对数据进行读取不能

修改,那么不能就该怎么导致不可重复读呢?困惑。

刚刚上面用的的是Transations的显示事务开发的吧,在MSDN中开发者建议用隐示的开发模式,即TransactionScope,因为隐示开发的话

事务由该基础结构自动管理。下面是OrderProcess中的一个事务:

1 using (TransactionScope ts =new TransactionScope(TransactionScopeOption.Required, tsTimeout)) {
2 // Receive the orders from the queue
3 for (int j =0; j < batchSize; j++) {
4
5 try {
6 //only receive more queued orders if there is enough time
7 if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds) {
8 queueOrders.Add(order.ReceiveFromQueue(queueTimeout));//从BLL.Order.ReceiveFromQueue得到订单队列,并将其放入到ArrayList中
9 }
10 else {
11 j = batchSize; // exit loop
12 }
13
14 //update elapsed time
15 elapsedTime =new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datetimeStarting.TotalSeconds;
16 }
17 catch (TimeoutException) {
18
19 //exit loop because no more messages are waiting
20 j = batchSize;
21 }
22 }
23
24 //process the queued orders
25 for (int k =0; k < queueOrders.Count; k++) {
26 order.Insert((OrderInfo)queueOrders[k]);//再调用BLL.Order.Insert方法吧得到的订单插入到Order和Invertory中
27 processedItems++;
28 totalOrdersProcessed++;
29 }
30
31 //batch complete or MSMQ receive timed out
32 ts.Complete();
33 }

在MSDN中有对TransactionScope进行详细的说明,这个类提供了三个构造函数,我们上面那段代码就用到了构造函数,

其中TransactionScopeOption是一个枚举,Required是表示:该范围需要一个事务。如果已经存在环境事务,则使用该环境事务。否则,在进入范围之前创建新的事务。这是默认值。

修改:我第一段代码的事务好像理解错了,SqkTransation事务从Begin到commit都算一个事务,很明显我上面理解成为两个事务了,根据事务的原子性和一致性,

delete和insert操作是要么执行要么不执行的。

原文地址:https://www.cnblogs.com/huaizuo/p/2105133.html