2013-10-21 84 views
2

如果我的事业,试图创建一个现有的表的错误,现有的交易似乎已经回滚本身:这是TRANSACTION对我来说是ROLLBACK(ed)吗?

private void CreateSomeThings() 
{ 
    SqlConnection SqlConn = new SqlConnection(ConnectionString); 
    SqlConn.Open(); 

    (new SqlCommand("BEGIN TRANSACTION", SqlConn)).ExecuteNonQuery(); 

    try 
    { 
     (new SqlCommand("CREATE TABLE sometable ([some_id] [int] IDENTITY(1,1) NOT NULL)", SqlConn)).ExecuteNonQuery(); 

     // Create the table again, but carry on by catching the exception 
     try 
     { 
      (new SqlCommand("CREATE TABLE sometable ([some_id] [int] IDENTITY(1,1) NOT NULL)", SqlConn)).ExecuteNonQuery(); 
     } 
     catch (Exception) 
     { 
     } 

     // If another exception is thrown 
     (new SqlCommand("bingy bongy boo", SqlConn)).ExecuteNonQuery(); 

     (new SqlCommand("COMMIT TRANSACTION", SqlConn)).ExecuteNonQuery(); 
    } 
    catch (Exception Ex) 
    { 
     try 
     { 
      // ... then this command will fail with "no corresponding BEGIN TRANSACTION" 
      (new SqlCommand("ROLLBACK TRANSACTION", SqlConn)).ExecuteNonQuery(); 
     } 
     catch (Exception Ex2) 
     { 
      throw; 
     } 
    } 
} 

我想知道这是怎么回事,为什么。我期望事务回滚是我的责任 - 其他错误它不会这样做:例如,如果我只是调用“bingy bongy”,则只会调用引发异常,然后我会在异常中执行ROLLBACK,而没有任何问题。

+0

你怎么知道?究竟是什么问题? – Szymon

+0

请参阅代码注释。异常处理程序中的ROLLBACK TRANSACTION失败。 – noelicus

+0

问题是“我不明白”,我想了解发生了什么,为什么。我希望事务回滚是我的责任 - 其他错误对我来说不会这样做。 – noelicus

回答

3

SQL Server可以单方面决定回滚事务。这是SQL Server中的一个严重的设计缺陷,因为您的应用程序永远无法知道事务是否仍处于活动状态。没有很好的文件记录什么样的错误回滚,什么样的错误没有。例如,我想我记得唯一的密钥违规和其他数据错误不会回滚。但其他人呢。有些错误甚至会终止连接(这很少见,而不是设计缺陷)。

我建议您以这种方式进行编码,以便在第一个错误处中止事务,然后失败或重试所有内容。这为您节省了很多麻烦。倾向于每批执行一条语句,否则您有冒险在事务之外运行第二条语句的风险。

如果你真的想保持错误后会必须做两两件事:

  1. 创建不回滚错误的白名单。在这种情况下,你可以继续下去。
  2. 请与SELECT @@TRANCOUNT核对交易是否仍然存在。
+0

通过每个批次执行一个语句,您是否有效地推荐不使用事务? – Szymon

+0

我想你在这里混淆了一些术语,虽然我不确定哪些。一个连接可以有多个事务,它们可以有多个可以有多个语句的批处理。每个ExecuteNonQuery调用都是一个批处理,它的'CommandText'中可以有多个语句。如果可能的话,使用交易,但每个批次陈述一次。 – usr

+0

你说得对。感谢您的解释。 – Szymon

0

您需要将事务对象传递给您用于使其参与同一事务的每个命令。

通常的模式是:

using (var conn = new SqlConnection("your connection string here")) 
{ 
    SqlTransaction trans = null; 
    try 
    { 
     conn.Open(); 
     trans = conn.BeginTransaction(); 

     using (SqlCommand command = new SqlCommand("command text here", conn, trans)) 
     { 
      // do your job 
     } 
     trans.Commit(); 
    } 
    catch (Exception ex) 
    { 
     try 
     { 
      // Attempt to roll back the transaction. 
      if (trans != null) trans.Rollback(); 
     } 
     catch (Exception exRollback) 
     { 
      // Throws an InvalidOperationException if the connection 
      // is closed or the transaction has already been rolled 
      // back on the server. 
     } 
    } 
} 
+0

如果我使用C#connection.BeginTransaction而不是事务变为无效(Connection在对象内变为空) - 即看起来行为方式相同。 – noelicus

+0

如果该事务的一部分是要创建一个已经存在的表,那么您的事务将不再有效 - 如果您*然后尝试执行trans.Rollback(),它将抛出另一个异常。我的问题是...为什么? – noelicus

+0

该事务已在服务器上回滚。尝试在SSMS中执行相同的操作 - 如果在事务内部的查询中存在错误,则会回滚它。我更新了我的答案以反映这一点。 – Szymon

相关问题