.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个例子中,有一个使用事务进行文件拷贝的例子。那里面有比较深入的实现。如果你还没有看过,推荐去研究一下,相信你读过此篇随笔,研究它应当不再是个难题。
一、事务使用基础
先看一段使用事务的代码:
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个例子中,有一个使用事务进行文件拷贝的例子。那里面有比较深入的实现。如果你还没有看过,推荐去研究一下,相信你读过此篇随笔,研究它应当不再是个难题。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件!
如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
桃源资源网 Design By www.nqtax.com
暂无“在.NET2.0中使用自定义事务操作”评论...
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。