如何强制外部库使用SQL事务

时间:2019-05-09 14:56:18

标签: c# sql .net sql-server

我有一个外部库,我将System.Data.SqlClient.SqlConnection的实例传递给该外部库,并且我想将该库在该连接中进行的所有操作包装在事务中。当我使用php / doctrine时,在这种情况下,我会完全做到这一点-在我的代码中启动事务,在库中调用发出DB查询的内容,然后在我的代码中提交事务。当我尝试在C#中使用这种方法时,出现以下异常:

  

ExecuteScalar要求分配给命令的连接处于挂起的本地事务中时,该命令必须具有事务。该命令的Transaction属性尚未初始化。

因此,我看了一下库代码,它始终使用SqlCommand而不设置Transaction属性。是否可以通过某种方式实现我的目标? (更改库代码是不可行的)

2 个答案:

答案 0 :(得分:1)

您尚未发布代码,但我认为您尝试通过调用SqlConnection.BeginTransaction()来使用显式事务。

您可以使用TransactionScope创建隐式事务。在TransactionScope生存期内创建的任何连接命令都将自动加入事务中。

Implementing an Implicit Transaction using Transaction Scope的示例复制:

    // Create the TransactionScope to execute the commands, guaranteeing
    // that both commands can commit or roll back as a single unit of work.
    using (TransactionScope scope = new TransactionScope())
    {
        using (SqlConnection connection1 = new SqlConnection(connectString1))
        {
            // Opening the connection automatically enlists it in the 
            // TransactionScope as a lightweight transaction.
            connection1.Open();

            // Create the SqlCommand object and execute the first command.
            SqlCommand command1 = new SqlCommand(commandText1, connection1);
            returnValue = command1.ExecuteNonQuery();
            writer.WriteLine("Rows to be affected by command1: {0}", returnValue);

            // If you get here, this means that command1 succeeded. By nesting
            // the using block for connection2 inside that of connection1, you
            // conserve server and network resources as connection2 is opened
            // only when there is a chance that the transaction can commit.   
            using (SqlConnection connection2 = new SqlConnection(connectString2))
            {
                // The transaction is escalated to a full distributed
                // transaction when connection2 is opened.
                connection2.Open();

                // Execute the second command in the second database.
                returnValue = 0;
                SqlCommand command2 = new SqlCommand(commandText2, connection2);
                returnValue = command2.ExecuteNonQuery();
                writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
            }
        }

        // The Complete method commits the transaction. If an exception has been thrown,
        // Complete is not  called and the transaction is rolled back.
        scope.Complete();

    }

此示例中的连接和两个命令都在单个事务下运行。如果发生异常,则事务将回滚。

答案 1 :(得分:0)

在.NET中,您可以使用TransationScope,所有操作都将在同一事务中发生:

using (TransactionScope scope = new TransactionScope())
{
    // Everything inside this block will be transactional:
    // Call the libraries which will use your SqlConnection here
}

或者您可以在调用其他库函数之前使用BeginTransaction,并在函数调用之后提交它