2009-04-24 97 views
3

我试图做一个快速的虚拟应用程序,所以我可以了解System.Transactions的来龙去脉。这个应用程序与2个不同的SQLExpress数据库进行交互。如果我在组件服务中提取事务统计信息,则可以在打开第二个连接时在outerScope中看到事务启动。如果failOuter为true,则事务中止,但不会抛出任何异常。当failInner为true时,会引发TransactionAbortedException。试图了解TransactionScope

从MSDN:

当你的应用程序完成所有它想要在交易完成工作,你应该调用Complete方法只有一次告知事务管理器,它是可以接受的提交事务。将调用完成为使用块中的最后一条语句是非常好的做法。

无法调用此方法会中止事务,因为事务管理器将此解释为系统故障,或等同于事务范围内引发的异常。

如果作用域创建事务并且事务被中止,则会引发TransactionAbortedException。

基于这一点,我期望我的outerScope抛出一个TransactionAbortedException,因为我的事务统计显示一个中止事务每次我运行我的应用程序与failOuter设置为true。我的方法返回true,因为即使事务中止也不会抛出异常。除非我放弃内部交易,否则它的行为如我所料。任何澄清将不胜感激。

public bool CreateNestedTransaction(bool failOuter, bool failInner) 
    { 
     try 
     { 
      using (TransactionScope outerScope = new TransactionScope()) 
      { 

       /* Perform transactional work here */ 
       using (SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=test1")) 
       { 
        SqlCommand myCommand = new SqlCommand(); 
        myConnection.Open(); 
        myCommand.Connection = myConnection; 

        myCommand.CommandText = "update test set Value = ((select Value from test where Id = (select max(Id) from test))+1) where Id = (select max(Id) from test)"; 
        myCommand.ExecuteNonQuery(); 
       } 


       using (SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=test1")) 
       { 
        SqlCommand myCommand = new SqlCommand(); 
        myConnection.Open(); 
        myCommand.Connection = myConnection; 

        myCommand.CommandText = "update test set Value = Value"; 
        myCommand.ExecuteNonQuery(); 
       } 

       using (TransactionScope innerScope = new TransactionScope()) 
       { 
        using (SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=test2")) 
        { 
         SqlCommand myCommand = new SqlCommand(); 
         myConnection.Open(); 
         myCommand.Connection = myConnection; 

         myCommand.CommandText = "update test set Value = ((select Value from test where Id = (select max(Id) from test))+1) where Id = (select max(Id) from test)"; 
         myCommand.ExecuteNonQuery(); 
        } 
        if (failInner == false) { innerScope.Complete(); } 
       } 

       if (failOuter == false) { outerScope.Complete(); } 
      } 
     } 

     catch (TransactionAbortedException) 
     { 
      return false; 
     } 

     return true; 
    } 

回答

7

通常你不会由于未能调用TransactionScope.Complete()的TransactionScope的超出范围之前得到处理抛出的异常。交易将会悄悄回滚。

由于您试图在外部TransactionScope上调用Complete,并且无法正确完成,因为内部TransactionScope已失败 - 因此会引发异常,您的情况中发生异常。

这有道理吗?

如果你想进行一些操作,如果你的外部事务中止,你可以尝试这样的事:内

// Inside each using TransactionScope(), hhok up the current transaction completed event 
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted); 

// handle the event somewhere else 
void Current_TransactionCompleted(object sender, TransactionEventArgs e) 
{ 
    // check the status of the transaction 
    if(e.Transaction.TransactionInformation.Status == TransactionStatus.Aborted) 
    // do something here 
} 

虽然我觉得对于一般的使用更清洁的方式将始终调用完成()你TransactionScope并处理任何产生的异常,如果你想做一些特定的事务失败。

+0

是的,这是有道理的。我认为MSDN的最后一行是把我抛弃。我正在看我的交易中止,并期待一个例外。感谢您的解释!有没有办法判断我的外部异常是否中止?我想在这种情况下返回false。 – 2009-04-24 18:57:47