SQL
SqlParameter
SqlParameterCollection
C#
using statement

The SqlParameter is already contained by another SqlParameterCollection - Does using cheat?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

The error The SqlParameter is already contained by another SqlParameterCollection occurs in ADO.NET when you try to add a SqlParameter object to a SqlCommand.Parameters collection while it is still attached to a different command's parameters. This commonly happens when reusing parameter objects across multiple commands or when a previous command was not properly disposed.

Why This Error Occurs

Each SqlParameter object can only belong to one SqlParameterCollection at a time. When you create a parameter and add it to a command, the parameter maintains a reference to that collection. If you then try to add the same parameter instance to another command, ADO.NET throws this error:

csharp
1// This throws the error
2var param = new SqlParameter("@Id", SqlDbType.Int) { Value = 42 };
3
4using var cmd1 = new SqlCommand("SELECT * FROM Users WHERE Id = @Id", connection);
5cmd1.Parameters.Add(param);
6
7using var cmd2 = new SqlCommand("SELECT * FROM Orders WHERE UserId = @Id", connection);
8cmd2.Parameters.Add(param); // ERROR: already contained by cmd1's collection

Does the using Statement Help?

The using statement disposes the SqlCommand, which calls Dispose() on the command object. However, disposing a SqlCommand does not automatically clear its Parameters collection or detach the parameters. This is a common misconception:

csharp
1SqlParameter param = new SqlParameter("@Id", SqlDbType.Int) { Value = 42 };
2
3// Even after disposing cmd1, param is still "attached"
4using (var cmd1 = new SqlCommand("SELECT * FROM Users WHERE Id = @Id", connection))
5{
6    cmd1.Parameters.Add(param);
7    cmd1.ExecuteNonQuery();
8} // cmd1 is disposed here, but param still references cmd1's collection
9
10using (var cmd2 = new SqlCommand("SELECT * FROM Orders WHERE UserId = @Id", connection))
11{
12    cmd2.Parameters.Add(param); // May still throw!
13}

The behavior depends on the .NET version and provider implementation, but you should never rely on disposal to detach parameters.

How to Fix It

Option 1: Clear Parameters Before Reuse

Explicitly clear the parameters collection before adding to a new command:

csharp
1var param = new SqlParameter("@Id", SqlDbType.Int) { Value = 42 };
2
3using var cmd1 = new SqlCommand("SELECT * FROM Users WHERE Id = @Id", connection);
4cmd1.Parameters.Add(param);
5cmd1.ExecuteNonQuery();
6cmd1.Parameters.Clear(); // Detaches param from cmd1
7
8using var cmd2 = new SqlCommand("SELECT * FROM Orders WHERE UserId = @Id", connection);
9cmd2.Parameters.Add(param); // Now works
10cmd2.ExecuteNonQuery();

The cleanest approach is to create new parameter instances for each command:

csharp
1using var cmd1 = new SqlCommand("SELECT * FROM Users WHERE Id = @Id", connection);
2cmd1.Parameters.AddWithValue("@Id", 42);
3cmd1.ExecuteNonQuery();
4
5using var cmd2 = new SqlCommand("SELECT * FROM Orders WHERE UserId = @Id", connection);
6cmd2.Parameters.AddWithValue("@Id", 42);
7cmd2.ExecuteNonQuery();

Option 3: Use a Helper Method

For complex scenarios where the same parameters appear in multiple queries, create a factory:

csharp
1SqlParameter CreateIdParam(int id)
2{
3    return new SqlParameter("@Id", SqlDbType.Int) { Value = id };
4}
5
6using var cmd1 = new SqlCommand("SELECT * FROM Users WHERE Id = @Id", connection);
7cmd1.Parameters.Add(CreateIdParam(42));
8cmd1.ExecuteNonQuery();
9
10using var cmd2 = new SqlCommand("SELECT * FROM Orders WHERE UserId = @Id", connection);
11cmd2.Parameters.Add(CreateIdParam(42));
12cmd2.ExecuteNonQuery();

Option 4: Clone the Parameter

If you need to reuse a parameter template:

csharp
1SqlParameter CloneParameter(SqlParameter source)
2{
3    return new SqlParameter(source.ParameterName, source.SqlDbType)
4    {
5        Value = source.Value,
6        Direction = source.Direction,
7        Size = source.Size
8    };
9}

Common Scenario: Loops

This error frequently appears in loops where commands are created but parameters are reused:

csharp
1// BAD: reusing the same parameter object
2var param = new SqlParameter("@Status", SqlDbType.NVarChar) { Value = "Active" };
3foreach (var table in tables)
4{
5    using var cmd = new SqlCommand($"SELECT * FROM {table} WHERE Status = @Status", connection);
6    cmd.Parameters.Add(param); // Fails on second iteration!
7}
8
9// GOOD: create new parameter each iteration
10foreach (var table in tables)
11{
12    using var cmd = new SqlCommand($"SELECT * FROM {table} WHERE Status = @Status", connection);
13    cmd.Parameters.AddWithValue("@Status", "Active");
14}

Common Pitfalls

  • Assuming using cleans up parameters: The using statement calls Dispose() on the command, but this does not reliably detach parameters from the collection. Always call Parameters.Clear() explicitly if reusing parameters.
  • AddWithValue type inference: AddWithValue infers the SQL type from the .NET type, which can cause performance issues (e.g., string maps to nvarchar(max) instead of nvarchar(50)). For performance-critical code, use Parameters.Add with explicit types.
  • Thread safety: SqlParameter objects are not thread-safe. Never share parameter instances across threads or concurrent commands.
  • Dapper and other ORMs: If you use Dapper or Entity Framework, they handle parameter creation internally, so this error does not apply. It is specific to raw ADO.NET usage.

Summary

  • Each SqlParameter can only belong to one SqlParameterCollection at a time
  • The using statement does not automatically detach parameters from a disposed command
  • Best practice: create new parameters for each command rather than reusing instances
  • If reusing is necessary, call cmd.Parameters.Clear() before adding parameters to a new command
  • In loops, always create fresh parameter instances per iteration

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.