在 C# 中使用 SqlTransaction,如何从存储过程中获取输出参数并插入另一个 table?

Using SqlTransaction in C#, how to get an output parameter from a stored procedure and insert into another table?

我有两个表:表 1 和表 2。我需要新插入的 Id(来自 Table1)作为输出参数,应该插入到 Table2 中。我必须在这里使用 SqlTransactionTransactionScope

存储过程:

    CREATE PROC InsertTable1
    (
        @Name nvarchar(50),
        @OutputParam int out
    )
    AS
    BEGIN
        INSERT INTO Table1(Name) 
        VALUES(@Name)

        SELECT @OutputParam = SCOPE_IDENTITY() 
    END

C# 代码:

SqlTransaction transaction = db.sqlconn.BeginTransaction();

SqlParameter[] param = new SqlParameter[2];
param[0] = new SqlParameter("@Name", "TestName");
param[1] = new SqlParameter("@OutputParam", SqlDbType.Int);
param[1].Direction = ParameterDirection.Output;

SqlHelper.ExecuteNonQuery(transaction, "InsertTable1", param);

int outputId = (int)param[1].Value;

当我 运行 调试项目时出现错误

Object reference not set to an instance of an object

就在

执行之后
int outputId = (int)param[1].Value;

我认为事务正在关闭连接之类的。如果我不使用交易,它会起作用。但无论如何我都必须使用交易。我愿意接受任何其他方法来实现相同的结果。

使用 OUTPUT 子句获取插入的数据并保存到临时 table.

Return 来自存储过程的数据

你不需要在输出中获取值,如果你想在另一个 table 中插入数据,在存储过程中你可以轻松地编写另一个插入查询,将这个 id 插入另一个 table

我看到您正在使用自定义 SqlHelper.ExecuteNonQuery 方法来执行查询,能否请您分享 SqlHelper.ExecuteNonQuery 方法

有关事务的问题请查看 TransactionScope class。这是微软 documentation.

的介绍部分

The TransactionScope class provides a simple way to mark a block of code as participating in a transaction, without requiring you to interact with the transaction itself. A transaction scope can select and manage the ambient transaction automatically. Due to its ease of use and efficiency, it is recommended that you use the TransactionScope class when developing a transaction application.

您还可以使用事务属性 (TransactionInformation and Current) 来在调试时调查事务的状态。这是我现在能想到的,还没有建议。

首先,您正在使用的 SqlHelper.ExecuteNonQuery 重载采用参数数组 values,而不是 SqlParameter 数组。它只能编译运行,因为类型是object。因此它将通过 SqlCommandBuilder 派生自己的参数,并尝试从您的参数参数中设置它们的值,但当然 SqlParameter 不能将 SqlParameter 作为其 .Value。 然而,这一点没有实际意义,因为源代码(下面)的注释明确指出您无法从此方法访问 OUTPUT 或 RETURN 值。

    /// <summary>
    /// Execute a stored procedure via a SqlCommand (that returns no resultset) against the specified 
    /// SqlTransaction using the provided parameter values.  This method will query the database to discover the parameters for the 
    /// stored procedure (the first time each stored procedure is called), and assign the values based on parameter order.
    /// </summary>
    /// <remarks>
    /// This method provides no access to output parameters or the stored procedure's return value parameter.
    /// 
    /// e.g.:  
    ///  int result = ExecuteNonQuery(conn, trans, "PublishOrders", 24, 36);
    /// </remarks>
    /// <param name="transaction">a valid SqlTransaction</param>
    /// <param name="spName">the name of the stored procedure</param>
    /// <param name="parameterValues">an array of objects to be assigned as the input values of the stored procedure</param>
    /// <returns>an int representing the number of rows affected by the command</returns>
    public static int ExecuteNonQuery(SqlTransaction transaction, string spName, params object[] parameterValues)
    {
        //if we receive parameter values, we need to figure out where they go
        if ((parameterValues != null) && (parameterValues.Length > 0))
        {
            //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
            SqlParameter[] commandParameters = SqlHelperParameterCache.GetSpParameterSet(transaction.Connection.ConnectionString, spName);

            //assign the provided values to these parameters based on parameter order
            AssignParameterValues(commandParameters, parameterValues);

            //call the overload that takes an array of SqlParameters
            return ExecuteNonQuery(transaction, CommandType.StoredProcedure, spName, commandParameters);
        }
        //otherwise we can just call the SP without params
        else
        {
            return ExecuteNonQuery(transaction, CommandType.StoredProcedure, spName);
        }
    }