当前位置:编程学习 > C#/ASP.NET >>

对TransactionScope的Complete方法的误用

由于找不到更好的方法,项目也没采用任何的第三方框架(如NHibernate),所以在业务层的事务处理部分我一直用TransactionScope这个东东来管理业务事务,微软把它翻译为范围事务,用它可以实现范围内的隐式事务,具体的使用方法本文就不多讲了,可以参看MSDN:http://msdn.microsoft.com/zh-cn/library/ms172152.aspx

今天我遇到一个问题,在方法A中引入TransactionScope,A中调用方法B,B中也引入了TransactionScope(TransactionScope是可以嵌套的哦),我的代码好多都是这么写的:

Code
 1public OperationResult A()
 2{
 3    OperationResult result = new OperationResult();
 4    int errorCount = 0;
 5
 6    using (TransactionScope tx = new TransactionScope())
 7    {
 8        try
 9        {
10            //TODO
11        }
12        catch
13        {
14            errorCount++;
15        }
16        if (B().Result != ResultInfo.操作成功)
17        {
18            errorCount++;
19        }
20        if (C().Result != ResultInfo.操作成功) //如果B此时失败了,调用C中的TransactionScope会出现“事务已终止”异常
21        {
22            errorCount++;
23        }
24        if (errorCount == 0)
25        {
26            result.Result = ResultInfo.操作成功;
27            tx.Complete();//我把complete全写在了这里
28        }
29    }
30
31    return result;
32}
33
34public OperationResult B()
35{
36    OperationResult result = new OperationResult();
37   
38    using (TransactionScope tx = new TransactionScope())
39    {
40        try
41        {
42            //TODO
43            tx.Complete();
44        }
45        catch
46        {
47            result.Result = ResultInfo.操作失败; //现在假设catch了,此时B中的TransactionScope并没有调用complete
48        }
49    }
50}
51
52public OperationResult C()
53{
54    OperationResult result = new OperationResult();
55   
56    //当B方法失败时,也就是B没执行complete时,调用到这里会出现异常“事务已终止”
57    using (TransactionScope tx = new TransactionScope())
58    {
59        //
60    }
61}
这样如果B中有方法执行不正确,就没有执行complete,这样,在被调用函数内部,直接终止了该环境事务,如果下面的代码中还有引用TransactionScope的,就会抛出“事务已终止”的异常。


我一直以为complete应该放在“正确的业务逻辑”里面,如果程序执行不正确就不要执行complete,它会自动回滚。我现在知道我的想法是灰常错误的,它是会回滚,它直接终止了都。以下是MSDN的解释:

 

当您对范围中的所有操作都已成功完成感到满意时,应该仅调用此方法一次,以通知事务管理器所有资源上的状态都是一致的,并且可以提交该事务。将该调用作为 using 块中的最后一个语句是很好的做法。

有关如何使用此方法的更多信息,请参见 使用事务范围实现隐式事务 主题。

未能调用此方法将中止事务,因为事务管理器将此解释为系统故障或在事务范围中引发了异常。但是还应该注意,调用此方法并不保证事务的提交。它只是一种将状态通知给事务管理器的方式。在调用此方法之后,就不能再通过 Current 属性访问环境事务,尝试这样做将导致引发异常。

如果 TransactionScope 对象创建事务,则资源管理器之间的实际提交工作发生在 End Using 语句处。如果它未创建事务,则在每当 CommittableTransaction 对象的所有者调用 Commit 时发生提交。届时事务管理器将调用资源管理器,并根据是否在 TransactionScope 对象上调用了此方法来通知它们进行提交或回滚。

 看这句“未能调用此方法将中止事务”,这也就是说,如果你不调用complete方法,事务直接终止,“因为事务管理器将此解释为系统故障或在事务范围中引发了异常”,所以现在我理解透彻了TransactionScope,它能保证你在它的生命周期内的隐式事务,无论你做什么数据库操作,它都会为你把这些操作全组织到一个环境事务中,如果你哪个环节出现问题,整个事务会隐式的回滚,complete不是告诉事务管理器马上开始提交,而只是通知事务管理器我的操作状态完成了,我以前把它当dbtransaction了,执行成功才submit,不成功rollback,而complete只是说“我的活干完了,你可以提交了”,如果你不在它的生命周期内调用这个方法,事务会中止,此时就像我上面写的方法B,当你再次尝试(using一个new TransactionScope)的时候,你会碰到“事务已终止”的错误。所以,我以后的代码修改成这样:

Code
 1public OperationResult A()
 2{
 3    OperationResult result = new OperationResult();
 4    //int errorCount = 0;这个标识变量可以扔了
 5
 6    using (TransactionScope tx = new TransactionScope())
 7    {
 8        try
 9        {
10            //TODO
11        }
12        catch
13        {
14            return result;
15        }
16        if (B().Result != ResultInfo.操作成功)
17        {
18            return result;
19        }
20        if (C().Result != ResultInfo.操作成功) //如果B此时失败了,调用C中的TransactionScope会出现“事务已终止”异常
21        {
22            return result;
23        }
24        result.Result = ResultInfo.操作成功;
25        tx.Complete();//现在全放在TransactionScope的生命周期末尾
26    }
27
28    return result;
29}
30
31public OperationResult B()
32{
33    OperationResult result = new OperationResult();

补充:软件开发 , C# ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,