SqlParameter已经包含在另一个SqlParameterCollection中- Does using() {}
本文关键字:using Does 包含 SqlParameterCollection 另一个 SqlParameter | 更新日期: 2023-09-27 18:12:06
当使用如下所示的using() {}
(sic)块时,假设cmd1
不存在于第一个using() {}
块的范围之外,为什么第二个块会抛出异常消息
SqlParameter已经包含在另一个SqlParameterCollection
这是否意味着附属于cmd1
的资源和/或句柄——包括参数(SqlParameterCollection
)——在块结束时被销毁时不会被释放?
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()将使您避免异常(和可能的尴尬)。
如果您需要复制,您可以使用以下脚本创建对象:
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
我怀疑SqlParameter
"知道"它是哪个命令的一部分,并且该信息在命令被处置时不被清除,但是当您调用command.Parameters.Clear()
时被清除。
我个人认为我会首先避免重用对象,但这取决于你:)
新增cmd.Parameters.Clear();执行后应该没问题
使用块并不能确保对象被"销毁",只需要调用Dispose()
方法即可。它实际做什么取决于具体的实现,在本例中,它显然没有清空集合。其思想是确保不会被垃圾收集器清理的非托管资源得到正确处置。由于Parameters集合不是非托管资源,因此不被dispose方法清除也就不足为奇了。
我遇到了这个特殊的错误,因为我使用相同的SqlParameter对象作为SqlParameter集合的一部分来多次调用一个过程。产生此错误的原因是SqlParameter对象与特定的SqlParameter Collection相关联,并且您不能使用相同的SqlParameter对象来创建新的SqlParameter Collection。
所以,不用这个:
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);
using
定义了一个作用域,并自动调用我们喜欢的Dispose()
。
超出作用域的引用不会使对象本身"消失",如果另一个对象有对它的引用,在这种情况下,parameters
有对cmd1
的引用。
我也有同样的问题,谢谢@Jon,基于我给出的例子。
当我调用以下函数时,其中传递了2次相同的sqlparameter。在第一次数据库调用中,它被正确调用,但在第二次调用时,给出了上述错误。
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;
}
我遇到这个异常是因为我没有实例化一个参数对象。我认为它是在抱怨两个过程具有相同名称的参数。它抱怨同一个参数被添加了两次。
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.
Issue
当我遇到这个问题时,我正在从c#执行SQL Server存储过程:
异常消息[SqlParameter已包含在另一个SqlParameterCollection.]
我传递了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);
<<p> 解决方案/strong> 为每个参数 添加以下代码行
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
我也有同样的异常。在我的情况下,我通过EntityFramework DBContext调用SQL。以下是我的代码,以及我是如何修改的。
破碎的代码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>()
的调用实际上返回了一个DbRawSqlQuery<T>
,它实现了IEnumerable
为什么调用。count()抛出异常?
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
}
我还没有启动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
}
Afterthough
我的项目可能是使用旧版本的EF。新版本可能已经通过清除参数或处理SqlCommand
对象修复了这个内部错误。
或者,可能有明确的指示告诉开发人员在迭代DbRawSqlQuery
之后不要调用.Count()
,我写错了。