Mientras usa los using() {}
bloques (sic) como se muestra a continuación, y asumiendo que cmd1
no vive más allá del alcance del primer using() {}
bloque, ¿por qué el segundo bloque debería lanzar una excepción con el mensaje?
El SqlParameter ya está contenido en otro SqlParameterCollection
¿Significa que los recursos y / o identificadores, incluidos los parámetros ( SqlParameterCollection
), adjuntos cmd1
no se liberan cuando se destruyen al final del bloque?
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: Hacer cmd1.Parameters.Clear () justo antes de la última llave del primer bloque using () {} lo salvará de la excepción (y posible vergüenza).
Si necesita reproducir, puede utilizar los siguientes scripts para crear los 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
fuente
fuente
Respuestas:
Sospecho que
SqlParameter
"sabe" de qué comando forma parte y que esa información no se borra cuando se elimina el comando, pero se borra cuando se llamacommand.Parameters.Clear()
.Personalmente, creo que evitaría reutilizar los objetos en primer lugar, pero depende de ti :)
fuente
Clear
antes de salir del primerusing
bloque. Hacerlo al ingresar a mi segundousing
bloque todavía arrojaba este error.El uso de bloques no asegura que un objeto sea "destruido", simplemente que
Dispose()
se llama al método. Lo que realmente hace depende de la implementación específica y, en este caso, claramente no vacía la colección. La idea es garantizar que los recursos no administrados que no serían limpiados por el recolector de basura se eliminen correctamente. Como la colección Parameters no es un recurso no administrado, no es del todo sorprendente que no se borre con el método dispose.fuente
Añadiendo cmd.Parameters.Clear (); después de la ejecución debería estar bien.
fuente
using
define un alcance y realiza la llamada automática deDispose()
para lo que nos encanta.Una referencia que se salga del alcance no hará que el objeto en sí "desaparezca" si otro objeto tiene una referencia a él, que en este caso será el caso de
parameters
tener una referenciacmd1
.fuente
También tengo el mismo problema Gracias @Jon, en base a eso di un ejemplo.
Cuando llamé a la siguiente función en la que pasó 2 veces el mismo parámetro sql. En la primera llamada a la base de datos, se llamó correctamente, pero en la segunda vez, se dio el error anterior.
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 es la función DAL común
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; }
fuente
Enfrenté este error en particular porque estaba usando los mismos objetos SqlParameter como parte de una colección SqlParameter para llamar a un procedimiento varias veces. En mi humilde opinión, la razón de este error es que los objetos SqlParameter están asociados a una colección SqlParameter particular y no puede usar los mismos objetos SqlParameter para crear una nueva colección SqlParameter.
Entonces, en lugar de esto:
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); */
Hacer esto:
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);
fuente
Encontré esta excepción porque no pude crear una instancia de un objeto de parámetro. Pensé que se estaba quejando de que dos procedimientos tenían parámetros con el mismo nombre. Se quejaba de que se agregaba dos veces el mismo parámetro.
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.
fuente
Problema
Estaba ejecutando un procedimiento almacenado de SQL Server desde C # cuando encontré este problema:
Porque
estaba pasando 3 parámetros a mi procedimiento almacenado. Agregué el
param = command.CreateParameter();
solo una vez en total. Debería haber agregado esta línea para cada parámetro, significa 3 veces en 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);
Solución
Agregar la siguiente línea de código para cada parámetro
param = command.CreateParameter();
fuente
¡Así es como lo he hecho!
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)); }
fuente
Si está utilizando EntityFramework
También tuve esta misma excepción. En mi caso, estaba llamando a SQL a través de EntityFramework DBContext. El siguiente es mi código y cómo lo arreglé.
Código roto
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 }
Nota: la llamada anterior a en
SqlQuery<T>()
realidad devuelve aDbRawSqlQuery<T>
, que implementaIEnumerable
¿Por qué llamar a .Count () lanza la excepción?
No he
.Count()
activado SQL Profiler para confirmar, pero sospecho que está activando otra llamada a SQL Server, e internamente está reutilizando el mismoSQLCommand
objeto y tratando de volver a agregar los parámetros duplicados.Solución / Código de trabajo
Agregué un contador dentro de mi
foreach
, para poder mantener un recuento de filas sin tener que llamar.Count()
int rowCount = 0; foreach(var row in rows) { rowCount++ // do something } if (rowCount == 0) { // tell user there are no rows }
En el futuro
Probablemente mi proyecto esté usando una versión anterior de EF. Es posible que la versión más nueva haya solucionado este error interno borrando los parámetros o eliminando el
SqlCommand
objeto.O tal vez, hay instrucciones explícitas que les dicen a los desarrolladores que no llamen
.Count()
después de iterar aDbRawSqlQuery
, y lo estoy codificando mal.fuente