Ao usar os using() {}
blocos (sic) conforme mostrado abaixo, e assumindo que cmd1
não está além do escopo do primeiro using() {}
bloco, por que o segundo bloco deve lançar uma exceção com a mensagem
O SqlParameter já está contido por outro SqlParameterCollection
Isso significa que os recursos e / ou identificadores - incluindo os parâmetros ( SqlParameterCollection
) - anexados cmd1
não são liberados quando são destruídos no final do bloco?
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);
}
}
}
NOTA: Fazer cmd1.Parameters.Clear () logo antes da última chave do primeiro bloco using () {} salvará você da exceção (e possível constrangimento).
Se precisar reproduzir, você pode usar os seguintes scripts para criar os objetos:
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
c#
.net
sql-server
ado.net
John Gathogo
fonte
fonte
Respostas:
Suspeito que
SqlParameter
"sabe" de qual comando ele faz parte e que essa informação não é apagada quando o comando é descartado, mas é apagada quando você chamacommand.Parameters.Clear()
.Pessoalmente, acho que evitaria reutilizar os objetos em primeiro lugar, mas depende de você :)
fonte
Clear
antes de sair do primeirousing
bloco. Fazer isso ao entrar no meu 2ºusing
bloco ainda gerou esse erro.O uso de blocos não garante que um objeto seja "destruído", apenas que o
Dispose()
método é chamado. O que isso realmente faz depende da implementação específica e, neste caso, claramente não esvazia a coleção. A ideia é garantir que os recursos não gerenciados que não seriam limpos pelo coletor de lixo sejam descartados corretamente. Como a coleção Parameters não é um recurso não gerenciado, não é totalmente surpreendente, pois não é limpa pelo método dispose.fonte
Adicionando cmd.Parameters.Clear (); após a execução deve estar bem.
fonte
using
define um escopo e faz a chamada automática deDispose()
que gostamos.Uma referência fora do escopo não fará com que o próprio objeto "desapareça" se outro objeto tiver uma referência a ele, o que, neste caso, será o caso para
parameters
ter uma referênciacmd1
.fonte
Também tenho o mesmo problema Obrigado @Jon, com base no que dei o exemplo.
Quando chamei a função abaixo na qual passou 2 vezes o mesmo parâmetro sql. Na primeira chamada ao banco de dados, ele foi chamado corretamente, mas na segunda vez, deu-se o erro acima.
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; }
Esta é a função DAL comum
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; }
fonte
Eu enfrentei esse erro específico porque estava usando os mesmos objetos SqlParameter como parte de uma coleção SqlParameter para chamar um procedimento várias vezes. A razão para esse erro IMHO é que os objetos SqlParameter estão associados a uma coleção SqlParameter específica e você não pode usar os mesmos objetos SqlParameter para criar uma nova coleção SqlParameter.
Então, em vez disso:
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); */
Faça isso:
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);
fonte
Encontrei essa exceção porque não consegui instanciar um objeto de parâmetro. Achei que fosse reclamar de dois procedimentos que tinham parâmetros com o mesmo nome. Ele estava reclamando sobre o mesmo parâmetro sendo adicionado duas vezes.
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.
fonte
Problema
Eu estava executando um procedimento armazenado do SQL Server em C # quando encontrei este problema:
Porque
eu estava passando 3 parâmetros para meu procedimento armazenado. Eu adicionei o
param = command.CreateParameter();
apenas uma vez. Eu deveria ter adicionado esta linha para cada parâmetro, isso significa 3 vezes no total.
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);
Solução
Adicionar a seguinte linha de código para cada parâmetro
param = command.CreateParameter();
fonte
É assim que eu fiz!
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)); }
fonte
Se você estiver usando EntityFramework
Eu também tive essa mesma exceção. No meu caso, eu estava chamando o SQL por meio de um EntityFramework DBContext. A seguir está meu código e como corrigi-lo.
Código Quebrado
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 }
Observação: a chamada acima para
SqlQuery<T>()
realmente retorna umDbRawSqlQuery<T>
, que implementaIEnumerable
Por que chamar .Count () lança a exceção?
Não iniciei o SQL Profiler para confirmar, mas suspeito que
.Count()
esteja disparando outra chamada para o SQL Server e, internamente, está reutilizando o mesmoSQLCommand
objeto e tentando adicionar novamente os parâmetros duplicados.Solução / Código de Trabalho
Eu adicionei um contador dentro do meu
foreach
, para que eu pudesse manter uma contagem de linhas sem ter que chamar.Count()
int rowCount = 0; foreach(var row in rows) { rowCount++ // do something } if (rowCount == 0) { // tell user there are no rows }
Depois
Meu projeto provavelmente está usando uma versão antiga do EF. A versão mais recente pode ter corrigido esse bug interno limpando os parâmetros ou eliminando o
SqlCommand
objeto.Ou talvez haja instruções explícitas que dizem aos desenvolvedores para não chamarem
.Count()
após a iteração de aDbRawSqlQuery
, e estou codificando errado.fonte