web-dev-qa-db-ja.com

SqlParameterは既に別のSqlParameterCollectionに含まれています-using(){}はチートしますか?

以下に示すようにusing() {}(sic)ブロックを使用し、_cmd1_が最初のusing() {}ブロックの範囲を超えて存在しないと仮定しているときに、なぜ2番目のブロックをスローする必要があるのかメッセージの例外

SqlParameterは既に別のSqlParameterCollectionに含まれています

ブロックの最後で破棄されたときに、_cmd1_に関連付けられたリソースやハンドル(パラメータ(SqlParameterCollection)を含む)は解放されないということですか?

_using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Test;Integrated Security=True"))
{
    var parameters = new SqlParameter[] { new SqlParameter("@ProductId", SqlDbType.Int ) };

    using(var cmd1 = new SqlCommand("SELECT ProductName FROM Products WHERE ProductId = @ProductId"))
    {
        foreach (var parameter in parameters)
        {
            cmd1.Parameters.Add(parameter);                
        }
        // cmd1.Parameters.Clear(); // uncomment to save your skin!
    }

    using (var cmd2 = new SqlCommand("SELECT Review FROM ProductReviews WHERE ProductId = @ProductId"))
    {
        foreach (var parameter in parameters)
        {
            cmd2.Parameters.Add(parameter);
        }
    }
}
_

注:最初の最後の中括弧の直前にcmd1.Parameters.Clear()を行うsing(){}ブロックは、例外(および可能性のある恥ずかしさ)からあなたを救います。

再現する必要がある場合は、次のスクリプトを使用してオブジェクトを作成できます。

_CREATE TABLE Products
(
    ProductId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    ProductName nvarchar(32) NOT NULL
)
GO

CREATE TABLE ProductReviews
(
    ReviewId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    ProductId int NOT NULL,
    Review nvarchar(128) NOT NULL
)
GO
_
74
John Gathogo

SqlParameterはどのコマンドの一部であるかを「知って」おり、コマンドが破棄されても情報はクリアされないが、command.Parameters.Clear()を呼び出すとisがクリアされると思う。

個人的には、そもそもオブジェクトの再利用は避けたいと思いますが、それはあなた次第です:)

93
Jon Skeet

ブロックを使用しても、オブジェクトが「破棄」されることは保証されず、単にDispose()メソッドが呼び出されるだけです。実際に行うことは特定の実装次第であり、この場合は明らかにコレクションを空にしない。アイデアは、ガベージコレクターによってクリーンアップされない管理されていないリソースが正しく破棄されるようにすることです。 Parametersコレクションはアンマネージリソースではないため、disposeメソッドによってクリアされないことを完全に保証しているわけではありません。

8
Ben Robinson

Cmd.Parameters.Clear()の追加;実行後は問題ないはずです。

4
Nish

usingはスコープを定義し、Dispose()の自動呼び出しを行います。

範囲外の参照は、別のオブジェクトがその参照を持っている場合、オブジェクト自体を「消失」させません。この場合、parameterscmd1を参照している場合です。

3
Jon Hanna

@Jonにも感謝します。例に基づいて同じ問題が発生しました。

2回同じsqlparameterが渡される以下の関数を呼び出したとき。最初のデータベース呼び出しでは適切に呼び出されましたが、2回目では上記のエラーが発生しました。

    public Claim GetClaim(long ClaimId)
    {
        string command = "SELECT * FROM tblClaim "
            + " WHERE RecordStatus = 1 and ClaimId = @ClaimId and ClientId =@ClientId";
        List<SqlParameter> objLSP_Proc = new List<SqlParameter>(){
                new SqlParameter("@ClientId", SessionModel.ClientId),
                new SqlParameter("@ClaimId", ClaimId)
            };

        DataTable dt = GetDataTable(command, objLSP_Proc);
        if (dt.Rows.Count == 0)
        {
            return null;
        }

        List<Claim> list = TableToList(dt);

        command = "SELECT * FROM tblClaimAttachment WHERE RecordStatus = 1 and ClaimId = @ClaimId and ClientId =@ClientId";

        DataTable dt = GetDataTable(command, objLSP_Proc); //gives error here, after add `sqlComm.Parameters.Clear();` in GetDataTable (below) function, the error resolved.


        retClaim.Attachments = new ClaimAttachs().SelectMany(command, objLSP_Proc);
        return retClaim;
    }

これは一般的なDAL関数です

       public DataTable GetDataTable(string strSql, List<SqlParameter> parameters)
        {
            DataTable dt = new DataTable();
            try
            {
                using (SqlConnection connection = this.GetConnection())
                {
                    SqlCommand sqlComm = new SqlCommand(strSql, connection);

                    if (parameters != null && parameters.Count > 0)
                    {
                        sqlComm.Parameters.AddRange(parameters.ToArray());
                    }

                    using (SqlDataAdapter da = new SqlDataAdapter())
                    {
                        da.SelectCommand = sqlComm;
                        da.Fill(dt);
                    }
                    sqlComm.Parameters.Clear(); //this added and error resolved
                }
            }
            catch (Exception ex)
            {                   
                throw;
            }
            return dt;
        }
2
Ajay2707

プロシージャを複数回呼び出すためにSqlParameterコレクションの一部として同じSqlParameterオブジェクトを使用していたため、この特定のエラーに直面しました。このエラーIMHOの理由は、SqlParameterオブジェクトが特定のSqlParameterコレクションに関連付けられており、同じSqlParameterオブジェクトを使用して新しいSqlParameterコレクションを作成できないことです。

だから、代わりに-

var param1 = new SqlParameter {DbType = DbType.String、ParameterName = param1、Direction = ParameterDirection.Input、Value = ""};

var param2 = new SqlParameter {DbType = DbType.Int64、ParameterName = param2、Direction = ParameterDirection.Input、Value = 100};

SqlParameter [] sqlParameter1 = new [] {param1、param2};

ExecuteProc(sp_name、sqlParameter1);

/ *エラー:

SqlParameter [] sqlParameter2 = new [] {param1、param2};

ExecuteProc(sp_name、sqlParameter2);

* /

これを行う-

var param3 = new SqlParameter {DbType = DbType.String、ParameterName = param1、Direction = ParameterDirection.Input、Value = param1.Value};

var param4 = new SqlParameter {DbType = DbType.Int64、ParameterName = param2、Direction = ParameterDirection.Input、Value = param2.Value};

SqlParameter [] sqlParameter3 = new [] {param3、param4}; ExecuteProc(sp_name、sqlParameter3);

1
SaCh

問題
この問題が発生したときに、C#からSQL Serverストアドプロシージャを実行していました。

例外メッセージ[SqlParameterは既に別のSqlParameterCollectionに含まれています。]

原因
ストアドプロシージャに3つのパラメーターを渡していました。私は追加しました

param = command.CreateParameter();

一度だけ。各パラメーターにこの行を追加する必要がありました。これは、全体で3倍を意味します。

DbCommand command = CreateCommand(ct.SourceServer, ct.SourceInstance, ct.SourceDatabase);
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[ETL].[pGenerateScriptToCreateIndex]";

DbParameter param = command.CreateParameter();
param.ParameterName = "@IndexTypeID";
param.DbType = DbType.Int16;
param.Value = 1;
command.Parameters.Add(param);

param = command.CreateParameter(); --This is the line I was missing
param.ParameterName = "@SchemaName";
param.DbType = DbType.String;
param.Value = ct.SourceSchema;
command.Parameters.Add(param);

param = command.CreateParameter(); --This is the line I was missing
param.ParameterName = "@TableName";
param.DbType = DbType.String;
param.Value = ct.SourceDataObjectName;
command.Parameters.Add(param);

dt = ExecuteSelectCommand(command);

ソリューション
各パラメーターに次のコード行を追加する

param = command.CreateParameter();
0
Goldfish

パラメータオブジェクトのインスタンス化に失敗したため、この例外が発生しました。同じ名前のパラメータを持つ2つのプロシージャについて文句を言っていると思いました。同じパラメーターが2回追加されるという不満がありました。

            Dim aParm As New SqlParameter()
            aParm.ParameterName = "NAR_ID" : aParm.Value = hfCurrentNAR_ID.Value
            m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)
            aParm = New SqlParameter
            Dim tbxDriveFile As TextBox = gvNetworkFileAccess.Rows(index).FindControl("tbxDriveFolderFile")
            aParm.ParameterName = "DriveFolderFile" : aParm.Value = tbxDriveFile.Text
            m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)
            **aParm = New SqlParameter()**  <--This line was missing.
            Dim aDDL As DropDownList = gvNetworkFileAccess.Rows(index).FindControl("ddlFileAccess")
            aParm.ParameterName = "AccessGranted" : aParm.Value = aDDL.Text
            **m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)** <-- The error occurred here.
0
Jon Boy