QA@IT

ストアドの戻り値について

13118 PV

C#からストアドを呼び出し、ストアドの実行結果をOUTPUTにて戻すようにしているのですが
テーブルロックをかけるとOUTPUTの変数に値を入れても呼出元に戻りません。
(ストアド上で@RetIDを見ると値が入っているが、C#側ではNullとなっている。)

環境
VisualStudio2008 C#
SQLServer2008

ストアドサンプルコード

CREATE PROCEDURE Proce_GetID
    @tempID INT
    ,@RetID INT OUTPUT
AS
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION

        --テーブルロック。コメントアウトすると正常に動く。
        SELECT *  FROM TABLE_A WITH (TABLOCKX)

        -- テーブルロックしていても@RetIDに値は入っています。
        SELECT @RetID = UserID FROM TABLE_A WHERE TempID = @tempID

        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION
    END CATCH
END

OUTPUTで値が戻らない原因をご存知の方いらっしゃいますでしょうか?

  • C# のコードはどうなっているんですか? 具体的に書いてください。 -

回答

C# 側のコードに問題があるのではないですか? アップされたコードはどう見ても変です。

Microsoft のサンプルデータベース Northwind の Products テーブルを使った以外は全く同じストアドで、以下の C# のコードで取得できます。 

ストアドプロシージャ

ALTER PROCEDURE dbo.Proce_GetID
    @tempID INT
    ,@RetID INT OUTPUT
AS
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION

        SELECT *  FROM Products WITH (TABLOCKX)

        SELECT @RetID = CategoryID FROM Products WHERE ProductID = @tempID

        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION
    END CATCH
END

C# 側のコード

protected int CreateDataSource(int id, DataTable table)
{
    string connString = "接続文字列";
    using (SqlConnection connection = new SqlConnection(connString))
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand("dbo.Proce_GetID", connection))
        {
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.AddWithValue("@tempID", id);

            SqlParameter param = new SqlParameter("@RetID", SqlDbType.Int);
            param.Direction = ParameterDirection.Output;
            command.Parameters.Add(param);

            using (SqlDataReader reader = command.ExecuteReader())
            {
                if (reader != null)
                {
                    table.Columns.Add(new DataColumn("ProductID", typeof(Int32)));
                    table.Columns.Add(new DataColumn("ProductName", typeof(string)));
                    table.Columns.Add(new DataColumn("SupplierID", typeof(Int32)));
                    table.Columns.Add(new DataColumn("CategoryID", typeof(Int32)));
                    table.Columns.Add(new DataColumn("UnitPrice", typeof(Decimal)));

                    while (reader.Read())
                    {
                        DataRow dr = table.NewRow();
                        dr["ProductID"] = (int)reader[0];
                        dr["ProductName"] = (string)reader[1];
                        dr["SupplierID"] = (int)reader[2];
                        dr["CategoryID"] = (int)reader[3];
                        dr["UnitPrice"] = (decimal)reader[5];
                        table.Rows.Add(dr);
                    }
                }
            }                

            // ストアドから取得した @RetID
            return (int)param.Value;
        }
    }
}
編集 履歴 (1)
  • ご回答ありがとうございます。
    ご指摘の通り、C#側に問題がありました。

    頂いたサンプルコードを基に組みなおすと正常に値を取得出来ました。

    違いが分からなかった為調査した結果、
    SqlDataReader reader
    を、解放してからでないと Output の値が取得出来ない事が分かりました。

    私のコードでも reader を解放してからでしたら値を取得出来ました。
    -

C#側です

SqlCommand command = new SqlCommand( "Proce_GetID", this.connection );
command.CommandType = CommandType.StoredProcedure;

command.Parameters.AddWithValue( "@tempID", 100 );
command.Parameters.Add( "@RetID", SqlDbType.Int );
command.Parameters["@RetID"].Direction = ParameterDirection.Output;
reader = command.ExecuteReader();

Hashtable data = new Hashtable();
int i = 0;
while ( reader.Read() )
{
data[reader.GetName(i)] = reader.GetInt32( i );
i++
}

この様な感じで取得しています。

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。