【问题标题】:Begin transaction in VB.net AND SQL Server 2008在 VB.net 和 SQL Server 2008 中开始事务
【发布时间】:2012-06-28 11:35:59
【问题描述】:

我在 vb.net 中开始交易。我在 SQL Server 2008 上执行了一个存储过程。该存储过程包含BEGIN TRANSACTION。它失败了,ROLLBACKCATCH 块中运行...

BEGIN CATCH

    IF @@TRANCOUNT > 1 ROLLBACK
        EXEC p_RethrowError

END CATCH

Rethrow 有效地执行了“raiserror”。

执行传递回 vb.net。执行“Catch sqlException”中的回滚。

问题:

  • 为什么@@TRANCOUNT 是 1 而不是 2? (即 vb.net 中的begin trans 怎么不包括在内?)

  • 为什么 SQL 中的ROLLBACK 也不会回滚客户端传输(但客户端中的回滚确实会回滚 SQL Server)?

最后,在 vb.net 中,如果您尝试在客户端内两次回滚事务,则会收到异常“事务已完成”。是否知道交易是否已完成或仍在等待中?谢谢。

----------- VB.Net 代码

Public Sub sub1(ByVal intID As Integer,  ByVal intValue as integer, ByVal intAuditUser As Int16)


Dim objConn As New SqlConnection(GetDBaseConnectionString())

    objConn.Open()

    '***** start the transaction ************************************************'
    Dim objTrans As SqlTransaction = objConn.BeginTransaction()

    Try

        Call sub2(objTrans, intID, intValue, intAuditUser)

        '***** commit the transaction ************************************************'
        objTrans.Commit()

    Catch es As SqlException
        objTrans.Rollback()
        Throw es

    Catch ex As Exception
        '***** rollback the transaction ************************************************'
        objTrans.Rollback()
        Throw ex

    Finally
        If objConn.State <> ConnectionState.Closed Then objConn.Close()
    End Try

End Sub

Private Sub Sub2(ByVal objTrans As SqlTransaction, ByVal intID As Integer, ByVal intValue as integer, ByVal intAuditUser As Int16)

    Dim objParams As New List(Of SqlParameter)

        SqlHelper.AddInParameter(objParams, "ID", SqlDbType.Int, intID)
        SqlHelper.AddInParameter(objParams, "Value", SqlDbType.Int, intValue)
        SqlHelper.AddInParameter(objParams, "AuditUser", SqlDbType.SmallInt, intAuditUser)

        '* save details'
        SqlHelper.ExecuteNonQuery(objTrans, CommandType.StoredProcedure, "p_StoredProc_UpdateSomething", objParams.ToArray)

End Sub

【问题讨论】:

  • 请添加一个代码示例,展示您如何在 vb.net 中开始交易
  • 回滚不是事务范围类的方法,所以我不知道您是如何“在客户端内回滚两次”
  • 对不起,我没有说清楚——在这个特定的例子中,我没有回滚客户端;但是,我尝试编写防御性代码,并且想知道是否有诸如“if transaction.pending then rollback”之类的语句。但仅就防御性编码而言,以防万一,无论出于何种原因,其他地方的另一个例程已经回滚(但在本例中没有)。
  • 的意思是说,在这个特定的例子中,我没有回滚客户端“两次”
  • 在您的代码块中,p_RethrowError 仅在@@TRANCOUNT > 1 时执行,但您问为什么@@TRANCOUNT = 1? p_RethrowError 没有执行吗?你有错误吗?

标签: sql-server vb.net sql-server-2008 transactionscope


【解决方案1】:

为什么@@TRANCOUNT 是 1 而不是 2? (即如何开始反式 不包括vb.net?)

是2。documented就是这样,如果不是2,服务器端ROLLBACK就不会执行。

为什么 SQL 中的 ROLLBACK 也不能回滚客户端事务(而是 客户端回滚是否回滚 SQL Server)?

没有单独的客户端事务。这都是同一个服务器端事务,ROLLBACK 始终是 rolls back 事务,无论当前的 @@TRANCOUNT 值如何,也无论在何处启动回滚。

最后,在 vb.net 中,如果您尝试回滚事务两次 在客户端中,您会收到异常“事务已完成”。

SqlTransaction.Rollback 的实现相当复杂,对于僵尸事务有不同的路径,SQL Server 2005 之前的版本和 SQL Server 2005 之后的版本,例如,2005 之前的原始路径执行一个简单的Rollback 调用,因此:

IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION

...并且SqlTransaction 客户端对象也进入僵尸模式,这就是为什么您在随后尝试再次对同一SqlTransaction 对象执行ROLLBACK 后看到错误的原因。

SQL Server 2008 上的代码路径类似,只是不再基于人类可读的 SQL。

这解释了为什么您只会在第二次和后续调用 SqlTransaction.Rollback 时收到错误,即使回滚已经在存储过程中不可撤销地启动了。

有吗 无论如何都知道交易是否已经完成,或者仍在 待处理?

是的。方法见here

获取与事务关联的SqlConnection对象,或者为null 如果交易不再有效。

如果可以这样测试:

if (myTransaction.Connection != null) ...

【讨论】:

  • 这很有帮助。你是对的,@@trancount 是 2。事实上,奇怪的是,当我在存储过程中没有 begintrans(但在客户端中有一个)时,@@trancount 出于某种原因仍然是 2。关于回滚(一个在客户端,一个在服务器) - 我理解你的意思是不要在客户端开始回滚:我的意思是,无论如何你都知道,在客户端调用回滚。我的意思是,如果存储过程执行了两次回滚,那么您会在第二次出现异常,但是我认为在 SP 中调用的回滚然后在客户端中调用的回滚不会导致相同的异常。无论如何,一切都很好,谢谢。
  • @DoctorChrisChris - 你是对的。客户端不知道 SP 中的回滚(客户端SqlTransaction 对象无法进入僵尸模式)。调用客户端回滚后,立即进入僵尸模式,不能调用两次客户端回滚。
  • 这就解释了,谢谢。对没有 begin tran 的 storeproc 的 @@Trancount=2 有什么想法?
  • @DoctorChrisChris - 我预计 1,你观察到 2,this guy 报告看到 0。无论如何,这里要注意的一件事是 implicit transactions
【解决方案2】:

嗨,克里斯·克里斯医生,

诚然,这与您最初的问题没有太大关系。但是,在阅读了您关于如何在 vb.net 中开始交易的评论之后。这可能会有所帮助:

Dim sqlUpdate As New StringBuilder 'use this to build your insert/update statements
sqlUpdate.Append("SQLCODE")
Dim cnADO As SqlClient.SqlConnection = yourDataBaseStuff.getConnection("targetServer", cnADO)
Dim trans As SqlTransaction = Nothing
Dim sqlCmdUpdate As New SqlCommand(sqlUpdate.ToString, cnADO)
sqlCmdUpdate.Parameters.AddWithValue("@X", new_x) 'where @X is your stored procedure variable, and new_x is what you would like to pass in


' Build Transaction, associate commands
trans = cnADO.BeginTransaction
cmdLoad.Transaction = trans
cmdLoad.CommandText = "Transaction stuff"
cmdLoad.ExecuteNonQuery()
sqlCmdUpdate.Transaction = trans
sqlCmdUpdate.ExecuteNonQuery()
trans.Commit()

希望这能让你朝着更好的方向开始!

-sf

【讨论】:

  • 感谢@Sacredfaith;我们曾经做过类似的事情,但现在我们只使用企业库 sqlhelper(见代码):让事情变得更容易和更快
猜你喜欢
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多