110 votes

Le SqlParameter est déjà contenu dans une autre SqlParameterCollection - Est-ce que using() {} triche ?

Lors de l'utilisation du using() {} (sic) comme indiqué ci-dessous, et en supposant que cmd1 ne vit pas au-delà du champ d'application de la première using() {} pourquoi le deuxième bloc devrait-il lancer une exception avec le message

Le SqlParameter est déjà contenu dans une autre SqlParameterCollection

Cela signifie-t-il que les ressources et/ou les poignées - y compris les paramètres ( SqlParameterCollection ) - rattaché à cmd1 ne sont pas libérés lorsqu'il est détruit à la fin du bloc ?

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

NOTE : En faisant cmd1.Parameters.Clear() juste avant la dernière accolade du premier using() {} vous évitera l'exception (et un éventuel embarras).

Si vous avez besoin de reproduire, vous pouvez utiliser les scripts suivants pour créer les objets :

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

134voto

Jon Skeet Points 692016

Je soupçonne que SqlParameter "sait" de quelle commande il fait partie, et que cette information n'est pas effacée lorsque la commande est supprimée, mais qu'il n'y a pas d'autre solution. est libéré lors de l'appel command.Parameters.Clear() .

Personnellement, je pense que j'éviterais de réutiliser les objets en premier lieu, mais c'est à vous de voir :)

12voto

Nish Points 101

L'ajout de cmd.Parameters.Clear() ; après l'exécution devrait suffire.

9voto

Ben Robinson Points 14558

L'utilisation de blocs ne garantit pas la "destruction" d'un objet, mais simplement que la fonction Dispose() est appelée. Ce que cela fait réellement dépend de l'implémentation spécifique et dans ce cas, il est clair que cela ne vide pas la collection. L'idée est de s'assurer que les ressources non gérées qui ne seraient pas nettoyées par le ramasse-miettes sont correctement éliminées. La collection de paramètres n'étant pas une ressource non gérée, il n'est pas vraiment surprenant qu'elle ne soit pas vidée par la méthode dispose.

5voto

SaCh Points 160

J'ai rencontré cette erreur particulière parce que j'utilisais les mêmes objets SqlParameter dans le cadre d'une collection SqlParameter pour appeler une procédure plusieurs fois. La raison de cette erreur, selon moi, est que les objets SqlParameter sont associés à une collection SqlParameter particulière et que vous ne pouvez pas utiliser les mêmes objets SqlParameter pour créer une nouvelle collection SqlParameter.

Donc, au lieu de cela :

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

Faites ceci :

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

4voto

Jon Hanna Points 40291

using définit un champ d'application et effectue l'appel automatique de Dispose() pour laquelle nous l'aimons.

Une référence tombant hors du champ d'application ne fera pas "disparaître" l'objet lui-même si un autre objet y fait référence, ce qui, dans ce cas, sera le cas pour les objets suivants parameters comportant une référence à cmd1 .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X