.net 2.0 framework 中新增了 System.Transactions 命名空间,其中提供的一系列接口和类使得在.net 2.0 中使用事务比起从前要方便了许多。有关在 .net 2.0 下操作数据库事务的文章已经有了很多,这里只提一下如何设计自定义事务操作。

  一、事务使用基础

  先看一段使用事务的代码:

1using (TransactionScope ts= new TransactionScope())
2"提交!");
6 enlistment.Done();
7 "The method or operation is not implemented.");
12 "准备!");
17 preparingEnlistment.Prepared();
18 "回滚!");
23 enlistment.Done();
24 "提交!i的值变为:" + _var.i.ToString());
18 enlistment.Done();
19 "The method or operation is not implemented.");
24 "回滚!i的值变为:" + _var.i.ToString());
35 enlistment.Done();
36 "");
47 Transaction.Current.EnlistDurable(guid, myEnlistment2, EnlistmentOptions.None);
48 "事务完成!");
7 scope1.Complete();
8 Console.WriteLine("退出区域之前,i的值为:" + atd.i.ToString());
9"退出区域之后,i的值为:" + atd.i.ToString());
  运行这一段代码,我们可以看到如下结果:

  事务完成!
  退出区域之前,i的值为:0
  提交!i的值变为:1
  退出区域之后,i的值为:1

  从输出结果来看,赋值操作被成功执行了。可是有没有感觉有些奇怪?先做个讨论:

  1、如果前面没有 Thread.Sleep(1000) 这一行,那么我们多半会看到最后一行的输出中,i 的值依然会是 0!为什么?想想就容易明白,这里对 Commit 方法是采用的异步调用,如同另开了一个线程。如果主线程不作等待的话,当输出的时候事务的 Commit 方法多半还没有被执行,输出的结果当然就会不对。

  2、这个例子中,赋值操作是在 Commit 方法中才实际执行的。但实际上就本例而言,我们也可以做个调整:将赋值操作放在 AssignIntVarValue 方法的最后去执行,然后把 Commit 方法中的赋值操作去掉。相关的代码变化如下:

1class SampleEnlistment2 : IEnlistmentNotification
2"");
18 Transaction.Current.EnlistDurable(guid, myEnlistment2, EnlistmentOptions.None);
19 i = newValue;
20 Console.WriteLine("提交前改变!i的值为:" + i.ToString());
21 "事务失败!");
5 //scope1.Complete();
6 Console.WriteLine("退出区域之前,i的值为:" + atd.i.ToString());
7"事务失败!");
6 //scope1.Complete();
7 Console.WriteLine("退出区域之前,i的值为:" + atd.i.ToString());
8"回滚!i的值变为:" + _oldValue.ToString());
9 enlistment.Done();
10}
  再次运行之,结果就对了:

  提交前改变!i的值为:1
  提交前改变!i的值为:2
  事务失败!
  退出区域之前,i的值为:2
  回滚!i的值变为:1
  回滚!i的值变为:0

  结果的正确其实并不是调用的顺序就对了,只是 Rollback 方法在执行的时候先检查一下 _newValue 的值是否与当前 i 的值一致,不一致的话就等上一会儿。在等待的过程中,另一个实例的 Rollback 方法被执行,而它检查发现是匹配的,所以就会回滚到 1。第一个 Rollback 等待结束后再检查发现匹配了,于是就回滚为 0。

  当然实际应用中,这种方法是极不可取的。且不说执行顺序依然会有很大的风险,光是设计方式就有大问题。那么在实际应用中我们应当如何去做呢?这里只提供一下设计思想,具体的实现代码不再列出了。

  在前面的例子中,两次赋值共进行了两次登记,这一点是引发不稳定性的起因。我们应当考虑,两次赋值依然只登记一次,在第一次赋值的时候,建立一个 SampleEnlistment2 的实例并在 AssignTransactDemo 中保存下来,并且 SampleEnlistment2 需要记录当前的操作。下一次赋值时,仍然使用这个实例,只进行操作记录即可。这样,当回滚的时候,它根据记录的反顺序执行回滚操作就可以了。

  再进一步呢?如果说有多个 Transaction 需要进行赋值操作呢?这时我们可以在 AssignTransactionDemo 类中加入一个 Dictionary<Transaction, SampleEnlistment2>,使用的时候根据 Transaction 去寻找相应的条目即可。

  本文讨论暂到此为止。在微软的101个例子中,有一个使用事务进行文件拷贝的例子。那里面有比较深入的实现。如果你还没有看过,推荐去研究一下,相信你读过此篇随笔,研究它应当不再是个难题。
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!