При использовании using() {}
(sic) блоков, как показано ниже, и при условии, что cmd1
они не выходят за рамки первого using() {}
блока, почему второй блок должен генерировать исключение с сообщением
SqlParameter уже содержится в другой коллекции SqlParameterCollection.
Означает ли это, что ресурсы и / или дескрипторы, включая параметры ( SqlParameterCollection
), прикрепленные к cmd1
, не освобождаются, когда они уничтожаются в конце блока?
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);
}
}
}
ПРИМЕЧАНИЕ. Выполнение cmd1.Parameters.Clear () непосредственно перед последней фигурной скобкой первого блока using () {} избавит вас от исключения (и возможного затруднения).
Если вам нужно воспроизвести, вы можете использовать следующие скрипты для создания объектов:
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
Джон Гатого
источник
источник
Ответы:
Я подозреваю , что
SqlParameter
«знает» , какая команда это часть, и что эта информация не удаляется , когда команда расположена, но это очищается , когда вы звонитеcommand.Parameters.Clear()
.Лично я думаю, что я бы вообще избегал повторного использования объектов, но решать вам :)
источник
Clear
перед выходом из первогоusing
блока. Выполнение этого при входе во второйusing
блок по-прежнему вызывает эту ошибку.Использование блоков не гарантирует, что объект «уничтожен», а просто
Dispose()
вызывает метод. Что это на самом деле делает, зависит от конкретной реализации, и в этом случае он явно не очищает коллекцию. Идея состоит в том, чтобы обеспечить правильное удаление неуправляемых ресурсов, которые не будут очищены сборщиком мусора. Поскольку коллекция Parameters не является неуправляемым ресурсом, неудивительно, что она не очищается методом dispose.источник
Добавление cmd.Parameters.Clear (); после казни должно быть все в порядке.
источник
using
определяет область действия и выполняет автоматический вызов,Dispose()
за который мы ее любим.Ссылка, выпадающая из области видимости, не приведет к «исчезновению» самого объекта, если на него есть ссылка на другой объект, что в данном случае будет иметь место при
parameters
наличии ссылкиcmd1
.источник
У меня тоже такая же проблема. Спасибо @Jon, на основе этого я привел пример.
Когда я вызвал функцию ниже, в которой 2 раза прошел один и тот же параметр sql. При первом вызове базы данных он был вызван правильно, но во второй раз возникла указанная выше ошибка.
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; }
Это обычная функция DAL
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; }
источник
Я столкнулся с этой конкретной ошибкой, потому что использовал одни и те же объекты SqlParameter как часть коллекции SqlParameter для многократного вызова процедуры. Причина этой ошибки IMHO заключается в том, что объекты SqlParameter связаны с определенной коллекцией SqlParameter, и вы не можете использовать те же объекты SqlParameter для создания новой коллекции SqlParameter.
Итак, вместо этого:
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); */
Сделай это:
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);
источник
Я столкнулся с этим исключением, потому что мне не удалось создать экземпляр объекта параметра. Я думал, что он жаловался на две процедуры с одинаковыми параметрами. Он жаловался на добавление одного и того же параметра дважды.
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.
источник
Проблема.
Когда я столкнулся с этой проблемой, я выполнял хранимую процедуру SQL Server из C #:
Потому что
я передавал в свою хранимую процедуру 3 параметра. Я добавил
param = command.CreateParameter();
только один раз вместе. Я должен был добавить эту строку для каждого параметра, то есть всего 3 раза.
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);
Решение
Добавление следующей строки кода для каждого параметра
param = command.CreateParameter();
источник
Вот как я это сделал!
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)); }
источник
Если вы используете EntityFramework
У меня тоже было такое же исключение. В моем случае я вызывал SQL через EntityFramework DBContext. Ниже приведен мой код и то, как я его исправил.
Сломанный код
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 }
Примечание: приведенный выше вызов
SqlQuery<T>()
фактически возвращает aDbRawSqlQuery<T>
, который реализуетIEnumerable
Почему вызов .Count () вызывает исключение?
Я не запускал SQL Profiler для подтверждения, но подозреваю, что
.Count()
это вызывает еще один вызов SQL Server, а внутри он повторно использует тот жеSQLCommand
объект и пытается повторно добавить повторяющиеся параметры.Решение / Рабочий код
Я добавил счетчик в свой
foreach
, чтобы я мог вести подсчет строк без вызова.Count()
int rowCount = 0; foreach(var row in rows) { rowCount++ // do something } if (rowCount == 0) { // tell user there are no rows }
Запоздало
В моем проекте, вероятно, используется старая версия EF. В новой версии эта внутренняя ошибка могла быть исправлена путем очистки параметров или удаления
SqlCommand
объекта.Или, может быть, есть явные инструкции, которые говорят разработчикам не вызывать
.Count()
после итерацииDbRawSqlQuery
, а я кодирую это неправильно.источник