Подтвердить что ты не робот

SqlParameter уже содержится другим SqlParameterCollection - использует ли() {} чит?

При использовании блоков using() {} (sic), как показано ниже, и при условии, что cmd1 не находится за пределами области действия первого блока using() {}, почему второй блок должен генерировать исключение с сообщением

SqlParameter уже содержится в другой SqlParameterCollection

Означает ли это, что ресурсы и/или дескрипторы, включая параметры (SqlParameterCollection), присоединенные к cmd1, не освобождаются, когда он уничтожается в конце блока?

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() непосредственно перед последней скобкой первого блока using() {} избавит вас от исключения (и возможного затруднения).

Если вам нужно воспроизвести, вы можете использовать следующие сценарии для создания объектов:

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
4b9b3361

Ответ 1

Я подозреваю, что SqlParameter "знает", который его выполняет, и что эта информация не очищается, когда команда расположена, но очищается, когда вы вызываете command.Parameters.Clear().

Лично я думаю, что избегаю повторного использования объектов в первую очередь, но это зависит от вас:)

Ответ 2

Использование блоков не гарантирует, что объект "уничтожен", просто вызывается метод Dispose(). То, что на самом деле происходит, зависит от конкретной реализации, и в этом случае он явно не пуст сбор. Идея состоит в том, чтобы гарантировать, что неуправляемые ресурсы, которые не будут очищены сборщиком мусора, будут правильно утилизированы. Поскольку набор параметров не является неуправляемым ресурсом, он не совсем удивляет, что метод dispose не очищается.

Ответ 3

Добавление cmd.Parameters.Clear(); после выполнения должно быть хорошо.

Ответ 4

using определяет область действия и выполняет автоматический вызов Dispose(), для которого мы его любим.

Ссылка, выпавшая из области действия, не заставит объект "исчезать", если другой объект имеет ссылку на него, что в данном случае будет иметь место для parameters, имеющего ссылку на cmd1.

Ответ 5

У меня также есть та же проблема, спасибо @Jon, основываясь на том, что я привел пример.

Когда я вызвал нижеприведенную функцию, в которой 2 раза один и тот же sqlparameter проходил. При первом вызове базы данных он был вызван правильно, но во второй раз он выдал вышеуказанную ошибку.

    public Claim GetClaim(long ClaimId)
    {
        string command = "SELECT * FROM tblClaim "
            + " WHERE RecordStatus = 1 and ClaimId = @ClaimId and ClientId [email protected]";
        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 [email protected]";

        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;
        }

Ответ 6

Я столкнулся с этой конкретной ошибкой, потому что я использовал те же объекты SqlParameter, что и часть коллекции SqlParameter, для многократного вызова процедуры. Причиной этой ошибки IMHO является то, что объекты SqlParameter связаны с определенной коллекцией SqlParameter, и вы не можете использовать те же объекты SqlParameter для создания новой коллекции SqlParameter.

Итак, вместо -

var param1 = новый SqlParameter {DbType = DbType.String, ParameterName = param1, Direction = ParameterDirection.Input, Value = ""};

var param2 = новый 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);

*/

Do this-

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);

Ответ 7

Я столкнулся с этим исключением, потому что мне не удалось создать экземпляр объекта параметра. Я думал, что он жаловался на две процедуры с параметрами с тем же именем. Он жаловался, что тот же параметр добавляется дважды.

            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.

Ответ 8

вопрос
Я выполнял хранимую процедуру 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();