天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

在.NET2.0中使用自定義事務(wù)操作

  .NET 2.0 framework 中新增了 System.Transactions 命名空間,其中提供的一系列接口和類使得在.NET 2.0 中使用事務(wù)比起從前要方便了許多。有關(guān)在 .NET 2.0 下操作數(shù)據(jù)庫事務(wù)的文章已經(jīng)有了很多,這里只提一下如何設(shè)計自定義事務(wù)操作。

  一、事務(wù)使用基礎(chǔ)

  先看一段使用事務(wù)的代碼:

1using (TransactionScope ts= new TransactionScope())
2{
3 //自定義操作
4 ts.Complete();
5}
  這里使用 using 語句定義了一段隱性事務(wù)。如果我們在該語句塊中加入一段對 SQL Server 操作的代碼,那么它們將會自動加入這個事務(wù)。可以看出,這種事務(wù)的使用方式是極其方便的。

  那么,有沒有可能在該語句塊中加入我們自己定義的事務(wù)操作,并且該操作能夠隨著整個事務(wù)塊的成功而提交,隨其失敗而回滾呢?答案當(dāng)然是可以的,否則我就不會寫這篇隨筆了。

  二、實現(xiàn)自定義事務(wù)操作

  根據(jù)事務(wù)的特性,我們可以推想:這個操作必須有實現(xiàn)提交和回滾之類動作的方法。沒錯,這就是 System.Transactions 命名空間中的 IEnlistmentNotification 接口。我們先寫一個最簡單的實現(xiàn):

1class SampleEnlistment1 : IEnlistmentNotification
2{
3 void IEnlistmentNotification.Commit(Enlistment enlistment)
4 {
5 Console.WriteLine("提交!");
6 enlistment.Done();
7 }
8
9 void IEnlistmentNotification.InDoubt(Enlistment enlistment)
10 {
11 throw new Exception("The method or operation is not implemented.");
12 }
13
14 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
15 {
16 Console.WriteLine("準(zhǔn)備!");
17 preparingEnlistment.Prepared();
18 }
19
20 void IEnlistmentNotification.Rollback(Enlistment enlistment)
21 {
22 Console.WriteLine("回滾!");
23 enlistment.Done();
24 }
25}
26
27
  好,定義完之后,還需要向事務(wù)管理器進行注冊,把它加入到當(dāng)前事務(wù)中去:

1using (TransactionScope ts= new TransactionScope())
2{
3 SampleEnlistment1 myEnlistment1 = new SampleEnlistment1();
4 Transaction.Current.EnlistVolatile(myEnlistment1, EnlistmentOptions.None);
5 ts.Complete();
6}
  執(zhí)行這一段代碼,我們可以得到以下的輸出:

  準(zhǔn)備!
  提交!

  先解釋一下,當(dāng)調(diào)用 ts.Complete() 方法的時候,表示事務(wù)已成功執(zhí)行。隨后,事務(wù)管理器就會尋找當(dāng)前所有已注冊的條目,也就是 IEnlistmentNotification 的每一個實現(xiàn),依次調(diào)用它們的 Prepare 方法,即通知每個條目做好提交準(zhǔn)備,當(dāng)所有條目都調(diào)用了 Prepared() 表示自己已經(jīng)準(zhǔn)備妥當(dāng)之后,再依次調(diào)用它們的 Commit 方法進行提交。如果其中有一個沒有調(diào)用 Prepared 而是調(diào)用了 ForceRollback 的話,整個事務(wù)都將回滾,此時事務(wù)管理器再調(diào)用每個條目的 Rollback 方法。

  而如果我們將前面的 ts.Complete() 行注釋掉,顯然執(zhí)行結(jié)果就將變?yōu)椋?br>
  回滾!

  三、一個實現(xiàn)賦值的自定義操作

  考慮一下,我們要實現(xiàn)一個事務(wù)賦值操作。該如何做法?以下是一個例子:

1class SampleEnlistment2 : IEnlistmentNotification
2{
3 public SampleEnlistment2(AssignTransactionDemo var, int newValue)
4 {
5 _var = var;
6 _oldValue = var.i;
7 _newValue = newValue;
8 }
9
10 private AssignTransactionDemo _var;
11 private int _oldValue;
12 private int _newValue;
13
14 void IEnlistmentNotification.Commit(Enlistment enlistment)
15 {
16 _var.i = _newValue;
17 Console.WriteLine("提交!i的值變?yōu)椋? + _var.i.ToString());
18 enlistment.Done();
19 }
20
21 void IEnlistmentNotification.InDoubt(Enlistment enlistment)
22 {
23 throw new Exception("The method or operation is not implemented.");
24 }
25
26 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
27 {
28 preparingEnlistment.Prepared();
29 }
30
31 void IEnlistmentNotification.Rollback(Enlistment enlistment)
32 {
33 _var.i = _oldValue;
34 Console.WriteLine("回滾!i的值變?yōu)椋? + _var.i.ToString());
35 enlistment.Done();
36 }
37}
38
39class AssignTransactionDemo
40{
41 public int i;
42
43 public void AssignIntVarValue(int newValue)
44 {
45 SampleEnlistment2 myEnlistment2 = new SampleEnlistment2(this, newValue);
46 Guid guid = new Guid("{3456789A-7654-2345-ABCD-098765434567}");
47 Transaction.Current.EnlistDurable(guid, myEnlistment2, EnlistmentOptions.None);
48 }
49}
50
51
  然后,這樣來使用:

1AssignTransactionDemo atd = new AssignTransactionDemo();
2atd.i = 0;
3using (TransactionScope scope1 = new TransactionScope())
4{
5 atd.AssignIntVarValue(1);
6 Console.WriteLine("事務(wù)完成!");
7 scope1.Complete();
8 Console.WriteLine("退出區(qū)域之前,i的值為:" + atd.i.ToString());
9}
10Thread.Sleep(1000);
11Console.WriteLine("退出區(qū)域之后,i的值為:" + atd.i.ToString());
  運行這一段代碼,我們可以看到如下結(jié)果:

  事務(wù)完成!
  退出區(qū)域之前,i的值為:0
  提交!i的值變?yōu)椋?
  退出區(qū)域之后,i的值為:1

  從輸出結(jié)果來看,賦值操作被成功執(zhí)行了。可是有沒有感覺有些奇怪?先做個討論:

  1、如果前面沒有 Thread.Sleep(1000) 這一行,那么我們多半會看到最后一行的輸出中,i 的值依然會是 0!為什么?想想就容易明白,這里對 Commit 方法是采用的異步調(diào)用,如同另開了一個線程。如果主線程不作等待的話,當(dāng)輸出的時候事務(wù)的 Commit 方法多半還沒有被執(zhí)行,輸出的結(jié)果當(dāng)然就會不對。

  2、這個例子中,賦值操作是在 Commit 方法中才實際執(zhí)行的。但實際上就本例而言,我們也可以做個調(diào)整:將賦值操作放在 AssignIntVarValue 方法的最后去執(zhí)行,然后把 Commit 方法中的賦值操作去掉。相關(guān)的代碼變化如下:

1class SampleEnlistment2 : IEnlistmentNotification
2{
3 void IEnlistmentNotification.Commit(Enlistment enlistment)
4 {
5 enlistment.Done();
6 }
7 //其它略
8}
9
10class AssignTransactionDemo
11{
12 public int i;
13
14 public void AssignIntVarValue(int newValue)
15 {
16 SampleEnlistment2 myEnlistment2 = new SampleEnlistment2(this, newValue);
17 Guid guid = new Guid("{3456789A-7654-2345-ABCD-098765434567}");
18 Transaction.Current.EnlistDurable(guid, myEnlistment2, EnlistmentOptions.None);
19 i = newValue;
20 Console.WriteLine("提交前改變!i的值為:" + i.ToString());
21 }
22}
23
24
  這樣,執(zhí)行結(jié)果將會變?yōu)椋?br>
  提交前改變!i的值為:1
  事務(wù)完成!
  退出區(qū)域之前,i的值為:1
  退出區(qū)域之后,i的值為:1

  3、在前面的基礎(chǔ)上,當(dāng)把調(diào)用的地方作如下改動,使事務(wù)失敗:

1using (TransactionScope scope1 = new TransactionScope())
2{
3 atd.AssignIntVarValue(1);
4 Console.WriteLine("事務(wù)失敗!");
5 //scope1.Complete();
6 Console.WriteLine("退出區(qū)域之前,i的值為:" + atd.i.ToString());
7}
  此時的執(zhí)行結(jié)果將變?yōu)椋?br>
  提交前改變!i的值為:1
  事務(wù)失敗!
  退出區(qū)域之前,i的值為:1
  回滾!i的值變?yōu)椋?
  退出區(qū)域之后,i的值為:0

  可見,事務(wù)已成功回滾。

  四、進一步的討論

  前面我們都是只進行了一次賦值操作,如果我們需要進行兩次呢?

1using (TransactionScope scope1 = new TransactionScope())
2{
3 atd.AssignIntVarValue(1);
4 atd.AssignIntVarValue(2);
5 Console.WriteLine("事務(wù)失敗!");
6 //scope1.Complete();
7 Console.WriteLine("退出區(qū)域之前,i的值為:" + atd.i.ToString());
8}
  這時的執(zhí)行結(jié)果將會是如何?我們當(dāng)然是希望回滾的時候,i 的值能先變回為 1,再變回為 0。但是實際結(jié)果呢?

  提交前改變!i的值為:1
  提交前改變!i的值為:2
  事務(wù)失敗!
  退出區(qū)域之前,i的值為:2
  回滾!i的值變?yōu)椋?
  回滾!i的值變?yōu)椋?
  退出區(qū)域之后,i的值為:1

  顯然,事務(wù)的回滾并沒有按照我們希望的順序來,是何原因?分析一下機制就能知道,事務(wù)管理器向每個條目發(fā)出回滾命令的時候只是發(fā)出了一個異步調(diào)用,并且很可能還是按登記的順序來發(fā)出的,這樣一來,Rollback 方法的調(diào)用順序顯然就不能保證了。

  這時,如果將 Rollback 方法作一個小調(diào)整:

1void IEnlistmentNotification.Rollback(Enlistment enlistment)
2{
3 while (_var.i != _newValue)
4 {
5 Thread.Sleep(500);
6 }
7 _var.i = _oldValue;
8 Console.WriteLine("回滾!i的值變?yōu)椋? + _oldValue.ToString());
9 enlistment.Done();
10}
  再次運行之,結(jié)果就對了:

  提交前改變!i的值為:1
  提交前改變!i的值為:2
  事務(wù)失敗!
  退出區(qū)域之前,i的值為:2
  回滾!i的值變?yōu)椋?
  回滾!i的值變?yōu)椋?

  結(jié)果的正確其實并不是調(diào)用的順序就對了,只是 Rollback 方法在執(zhí)行的時候先檢查一下 _newValue 的值是否與當(dāng)前 i 的值一致,不一致的話就等上一會兒。在等待的過程中,另一個實例的 Rollback 方法被執(zhí)行,而它檢查發(fā)現(xiàn)是匹配的,所以就會回滾到 1。第一個 Rollback 等待結(jié)束后再檢查發(fā)現(xiàn)匹配了,于是就回滾為 0。

  當(dāng)然實際應(yīng)用中,這種方法是極不可取的。且不說執(zhí)行順序依然會有很大的風(fēng)險,光是設(shè)計方式就有大問題。那么在實際應(yīng)用中我們應(yīng)當(dāng)如何去做呢?這里只提供一下設(shè)計思想,具體的實現(xiàn)代碼不再列出了。

  在前面的例子中,兩次賦值共進行了兩次登記,這一點是引發(fā)不穩(wěn)定性的起因。我們應(yīng)當(dāng)考慮,兩次賦值依然只登記一次,在第一次賦值的時候,建立一個 SampleEnlistment2 的實例并在 AssignTransactDemo 中保存下來,并且 SampleEnlistment2 需要記錄當(dāng)前的操作。下一次賦值時,仍然使用這個實例,只進行操作記錄即可。這樣,當(dāng)回滾的時候,它根據(jù)記錄的反順序執(zhí)行回滾操作就可以了。

  再進一步呢?如果說有多個 Transaction 需要進行賦值操作呢?這時我們可以在 AssignTransactionDemo 類中加入一個 Dictionary<Transaction, SampleEnlistment2>,使用的時候根據(jù) Transaction 去尋找相應(yīng)的條目即可。

  本文討論暫到此為止。在微軟的101個例子中,有一個使用事務(wù)進行文件拷貝的例子。那里面有比較深入的實現(xiàn)。如果你還沒有看過,推薦去研究一下,相信你讀過此篇隨筆,研究它應(yīng)當(dāng)不再是個難題。

AspNet技術(shù)在.NET2.0中使用自定義事務(wù)操作,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 国产成人自拍视频在线观看 | 99re久久超碰视频精品 | 亚洲AV精品乱码专区 | 性色少妇AV蜜臀人妻无码 | 午夜dj影院视频观看 | 青草影院天堂男人久久 | yellow在线观看免费高清的日本 | 一本道本线中文无码 | 全黄H全肉细节文短篇 | qvod12| 久久成人免费观看草草影院 | 高清无码中文字幕在线观看视频 | 中文人妻熟妇精品乱又伦 | 接吻吃胸摸下面啪啪教程 | 漂亮的保姆6在线观看中文 漂亮的保姆5电影免费观看完整版中文 | 蜜芽一二三区 | 妻子的妹妹在线 | 色综合久久五月 | 色狠狠一区二区 | 欧美日韩中文国产一区发布 | 777米奇影院第七色色 | 欧美 国产 日产 韩国 在线 | 国产强奷糟蹋漂亮邻居在线观看 | 5G在线观看免费年龄确认 | 又色又爽又黄gif动态视频 | 高清一区二区亚洲欧美日韩 | 国产色婷婷精品人妻蜜桃成熟时 | 儿媳妇完整版视频播放免费观看 | 哺乳溢出羽月希中文字幕 | 麻豆狠色伊人亚洲综合网站 | 亚洲视频在线观看视频 | 亚洲中文字幕无码一去台湾 | 国产精品无码久久av | 亚洲人人为我我为人人 | 色噜噜噜亚洲男人的天堂 | 精品一区二区三区在线成人 | 成人免费在线观看视频 | 国产人妻人伦精品59HHH | 国产精品欧美一区二区在线看 | 青青草久久 | 国产高潮国产高潮久久久久久 |