SqlParameter는 이미 다른 SqlParameterCollection에 포함되어 있습니다. using () {} 치트가 있습니까?


85

은 USING 동안 using() {}아래와 같이 (SiC)를 차단하고, 그 가정 cmd1제의 범위를 넘어 살지 않는 using() {}블록, 제 2 블록 이유는 메시지가 예외를 발생한다

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

참고 : 첫 번째 using () {} 블록 의 마지막 중괄호 바로 앞에 cmd1.Parameters.Clear ()를 수행하면 예외 (및 가능한 당황)에서 벗어날 수 있습니다.

재현해야하는 경우 다음 스크립트를 사용하여 개체를 만들 수 있습니다.

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

나는 이것도보고 있지만 그 수정이 작동하지 않았습니다. 실망 스럽네요. 그리고 재사용이 아닌 단일 cmd 객체 만 사용하고 있습니다. 비동기 재시도 루프에 래핑되어 있으므로 동일한 방식으로 피하지 않은 동일한 근본 원인 일 수 있습니다.
Ed Williams

답변:


109

나는 용의자 SqlParameter의 그것의 부분을 명령하고, 그 정보는 명령이 배치이지만, 경우 삭제되지 않도록하는 "인식" 되는 전화 할 때 지워 command.Parameters.Clear().

개인적으로 나는 처음에 객체를 재사용하는 것을 피할 것이라고 생각하지만 그것은 당신에게 달려 있습니다 :)


1
감사. 나는 그것이 사실이라고 생각했다. 또한 SqlParameter가 삭제 된 객체와 연관되어 있다는 것을 의미합니다.이 객체는 좋은 것인지 잘 모르겠습니다
John Gathogo

@JohnGathogo : 음, 그것은 연관이 형성된 후에 폐기되는 객체와 연관되어 있습니다. 확실히 이상적이지 않습니다.
Jon Skeet 2011 년

11
다른 사람을위한 메모. 나는 Clear첫 번째 using블록을 나가기 전에 수행해야했다 . 내 두 번째 using블록에 들어갈 때 수행하면 여전히이 오류가 발생했습니다.
Snekse

9

블록을 사용한다고해서 객체가 "파괴"되는 것은 아니며 단순히 Dispose()메서드가 호출 된다는 것 입니다. 실제로하는 일은 특정 구현에 달려 있으며이 경우 컬렉션을 비우지 않습니다. 아이디어는 가비지 수집기에 의해 정리되지 않는 관리되지 않는 리소스가 올바르게 삭제되도록하는 것입니다. Parameters 컬렉션은 관리되지 않는 리소스가 아니기 때문에 dispose 메서드에 의해 지워지지 않는다고 전적으로 의심하지 않습니다.


7

cmd.Parameters.Clear (); 추가 실행 후 괜찮을 것입니다.


3

using범위를 정의하고 Dispose()우리가 좋아 하는 자동 호출을 수행 합니다.

범위를 벗어난 참조는 다른 객체에 참조가있는 경우 객체 자체가 "사라지지"않습니다.이 경우를 parameters참조 하는 경우가 됩니다 cmd1.


2

나는 또한 내가 예제를 준 것에 근거하여 @Jon에게 감사드립니다.

동일한 sqlparameter가 두 번 전달 된 아래 함수를 호출했을 때. 첫 번째 데이터베이스 호출에서는 제대로 호출되었지만 두 번째에서는 위의 오류가 발생했습니다.

    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

프로 시저를 여러 번 호출하기 위해 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);

/*ERROR : 
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);

0

매개 변수 개체를 인스턴스화하지 못했기 때문에이 예외가 발생했습니다. 같은 이름의 매개 변수를 가진 두 개의 프로 시저에 대해 불평하고 있다고 생각했습니다. 동일한 매개 변수가 두 번 추가되는 것에 대해 불평했습니다.

            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

실행
나는이 문제가 발생했을 때이 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

이것이 내가 한 방법입니다!

        ILease lease = (ILease)_SqlParameterCollection.InitializeLifetimeService();
        if (lease.CurrentState == LeaseState.Initial)
        {
            lease.InitialLeaseTime = TimeSpan.FromMinutes(5);
            lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
            lease.RenewOnCallTime = TimeSpan.FromMinutes(2);
            lease.Renew(new TimeSpan(0, 5, 0));
        }

0

EntityFramework를 사용하는 경우

나는 또한 이와 같은 예외가 있었다. 제 경우에는 EntityFramework DBContext를 통해 SQL을 호출했습니다. 다음은 내 코드와 수정 방법입니다.

깨진 코드

string sql = "UserReport @userID, @startDate, @endDate";

var sqlParams = new Object[]
{
    new SqlParameter { ParameterName= "@userID", Value = p.UserID, SqlDbType = SqlDbType.Int, IsNullable = true }
    ,new SqlParameter { ParameterName= "@startDate", Value = p.StartDate, SqlDbType = SqlDbType.DateTime, IsNullable = true }
    ,new SqlParameter { ParameterName= "@endDate", Value = p.EndDate, SqlDbType = SqlDbType.DateTime, IsNullable = true }
};

IEnumerable<T> rows = ctx.Database.SqlQuery<T>(sql,parameters);

foreach(var row in rows) {
    // do something
}

// the following call to .Count() is what triggers the exception
if (rows.Count() == 0) {
    // tell user there are no rows
}

참고 : 위의 호출은 SqlQuery<T>()실제로를 반환합니다 DbRawSqlQuery<T>.IEnumerable

.Count () 호출시 예외가 발생하는 이유는 무엇입니까?

확인을 위해 SQL 프로파일 러를 .Count()실행 하지는 않았지만 SQL Server에 대한 또 다른 호출을 트리거하고 내부적으로 동일한 SQLCommand개체를 재사용하고 중복 매개 변수를 다시 추가하려고합니다.

솔루션 / 작업 코드

foreach호출하지 않고도 행 수를 유지할 수 있도록 내 내부에 카운터를 추가했습니다..Count()

int rowCount = 0;

foreach(var row in rows) {
    rowCount++
    // do something
}

if (rowCount == 0) {
    // tell user there are no rows
}

그래도

내 프로젝트는 아마도 이전 버전의 EF를 사용하고있을 것입니다. 최신 버전은 매개 변수를 지우거나 SqlCommand개체 를 삭제하여이 내부 버그를 수정했을 수 있습니다 .

또는 개발자에게 .Count()를 반복 한 후 호출하지 말라고 지시하는 명시적인 지침이 있는데 DbRawSqlQuery, 제가 ​​잘못 코딩하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.