日期:2010-09-10  浏览次数:20564 次

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

  一、事务使用基础

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

1using (TransactionScope ts= new TransactionScope())
2{
3 //自定义操作
4 ts.Complete();
5}

  这里使用 using 语句定义了一段隐性事务。如果我们在该语句块中加入一段对 SQL Server 操作的代码,那么它们将会自动加入这个事务。可以看出,这种事务的使用方式是极其方便的。

  那么,有没有可能在该语句块中加入我们自己定义的事务操作,并且该操作能够随着整个事务块的成功而提交,随其失败而回滚呢?答案当然是可以的,否则我就不会写这篇随笔了。

  二、实现自定义事务操作

  根据事务的特性,我们可以推想:这个操作必须有实现提交和回滚之类动作的方法。没错,这就是 System.Transactions 命名空间中的 IEnlistmentNotification 接口。我们先写一个最简单的实现:

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("准备!");
17 preparingEnlistment.Prepared();
18 }
19
20 void IEnlistmentNotification.Rollback(Enlistment enlistment)
21 {
22 Console.WriteLine("回滚!");
23 enlistment.Done();
24 }
25}
26
27

  好,定义完之后,还需要向事务管理器进行注册,把它加入到当前事务中去:

1using (TransactionScope ts= new TransactionScope())
2{
3 SampleEnlistment1 myEnlistment1 = new SampleEnlistment1();
4 Transaction.Current.EnlistVolatile(myEnlistment1, EnlistmentOptions.None);
5 ts.Complete();
6}

  执行这一段代码,我们可以得到以下的输出:

  准备!
  提交!

  先解释一下,当调用 ts.Complete() 方法的时候,表示事务已成功执行。随后,事务管理器就会寻找当前所有已注册的条目,也就是 IEnlistmentNotification 的每一个实现,依次调用它们的 Prepare 方法,即通知每个条目做好提交准备,当所有条目都调用了 Prepared() 表示自己已经准备妥当之后,再依次调用它们的 Commit 方法进行提交。如果其中有一个没有调用 Prepared 而是调用了 ForceRollback 的话,整个事务都将回滚,此时事务管理器再调用每个条目的 Rollback 方法。

  而如果我们将前面的 ts.Complete() 行注释掉,显然执行结果就将变为:

  回滚!

  三、一个实现赋值的自定义操作

  考虑一下,我们要实现一个事务赋值操作。该如何做法?以下是一个例子:

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的值变为:" + _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的值变为:" + _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("事务完成!");
7 scope1.Complete();
8 Consol