c# - El parámetro SqlParameter ya está contenido en otra colección SqlParameterCollection-¿El uso de(){} engaña?
.net sql-server (6)
Añadiendo cmd.Parameters.Clear (); Después de la ejecución debe estar bien.
Al usar los bloques using () {} (sic) como se muestra a continuación, y suponiendo que cmd1 no vive más allá del alcance del primer bloque using () {} , ¿por qué el segundo bloque debe lanzar una excepción con el mensaje El parámetro SqlParameter es ¿Ya está contenido por otro SqlParameterCollection ? ¿Significa que los recursos y / o los manejadores, incluidos los Parámetros (SqlParameterCollection), conectados a 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 usando () {} lo salvará de la excepción (y de la posible vergüenza).
Si necesita reproducir, puede usar 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
El uso de bloques no garantiza que un objeto se "destruya", simplemente se llama al método Dispose()
. 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 el recolector de basura no pueda eliminar se eliminen correctamente. Como la colección de Parámetros no es un recurso no administrado, no es del todo sorprendente que no se borre con el método de disposición.
Encontré esta excepción porque no había podido crear una instancia de un objeto de parámetro. Pensé que se quejaba de que dos procedimientos tenían parámetros con el mismo nombre. Se quejaba de que el mismo parámetro se agregó dos veces.
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.
Me enfrenté a este error en particular porque estaba usando los mismos objetos SqlParameter como parte de una colección de SqlParameter para llamar a un procedimiento varias veces. El motivo de este error IMHO es que los objetos SqlParameter están asociados a una determinada colección SqlParameter y no puede usar los mismos objetos SqlParameter para crear una nueva colección SqlParameter.
Entonces, en lugar de ...
var param1 = nuevo SqlParameter {DbType = DbType.String, ParameterName = param1, Direction = ParameterDirection.Input, Value = ""};
var param2 = nuevo 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 = nuevo SqlParameter {DbType = DbType.String, ParameterName = param1, Direction = ParameterDirection.Input, Value = param1.Value};
var param4 = nuevo SqlParameter {DbType = DbType.Int64, ParameterName = param2, Direction = ParameterDirection.Input, Value = param2.Value};
SqlParameter [] sqlParameter3 = new [] {param3, param4}; ExecuteProc (sp_name, sqlParameter3);
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 llama command.Parameters.Clear()
.
Personalmente creo que evitaría reutilizar los objetos en primer lugar, pero depende de usted :)
using
define un alcance, y hace la llamada automática de Dispose()
por lo que nos encanta.
Una referencia fuera del alcance no hará que el objeto "desaparezca" si otro objeto tiene una referencia a él, lo que en este caso será el caso para los parameters
tienen una referencia a cmd1
.