Wenn Sie die using() {}
(sic) -Blöcke wie unten gezeigt verwenden und davon ausgehen, dass cmd1
sie nicht über den Umfang des ersten using() {}
Blocks hinausgehen , warum sollte der zweite Block eine Ausnahme mit der Nachricht auslösen?
Der SqlParameter ist bereits in einer anderen SqlParameterCollection enthalten
Bedeutet dies, dass Ressourcen und / oder Handles - einschließlich der Parameter ( SqlParameterCollection
) -, an die sie angehängt cmd1
sind, nicht freigegeben werden, wenn sie am Ende des Blocks zerstört werden?
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);
}
}
}
HINWEIS: Wenn Sie cmd1.Parameters.Clear () kurz vor der letzten Klammer des ersten using () {} -Blocks ausführen , werden Sie vor der Ausnahme (und möglichen Verlegenheit) bewahrt .
Wenn Sie reproduzieren müssen, können Sie die Objekte mit den folgenden Skripten erstellen:
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
quelle
Clear
bevor ich den erstenusing
Block verließ. Wenn ich meinen zweitenusing
Block betrete, wird dieser Fehler immer noch ausgelöst.Die Verwendung von Blöcken stellt nicht sicher, dass ein Objekt "zerstört" wird, sondern lediglich, dass die
Dispose()
Methode aufgerufen wird. Was dies tatsächlich tut, hängt von der spezifischen Implementierung ab und in diesem Fall wird die Sammlung eindeutig nicht geleert. Damit soll sichergestellt werden, dass nicht verwaltete Ressourcen, die vom Garbage Collector nicht bereinigt werden, ordnungsgemäß entsorgt werden. Da die Parameters-Auflistung keine nicht verwaltete Ressource ist, ist es nicht ganz überraschend, dass sie nicht durch die dispose-Methode gelöscht wird.quelle
Hinzufügen von cmd.Parameters.Clear (); nach der Ausführung sollte in Ordnung sein.
quelle
using
definiert einen Bereich und führt den automatischen Aufruf aus,Dispose()
für den wir ihn lieben.Eine Referenz, die außerhalb des Gültigkeitsbereichs liegt, lässt das Objekt selbst nicht "verschwinden", wenn auf ein anderes Objekt eine Referenz verweist, was in diesem Fall der Fall ist,
parameters
wenn auf eine Referenz verwiesen wirdcmd1
.quelle
Ich habe auch das gleiche Problem Danke @Jon, basierend auf dem, was ich Beispiel gegeben habe.
Als ich die folgende Funktion aufrief, in der 2 mal der gleiche SQL-Parameter übergeben wurde. Beim ersten Datenbankaufruf wurde es ordnungsgemäß aufgerufen, beim zweiten Mal wurde der obige Fehler ausgegeben.
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; }
Dies ist die übliche DAL-Funktion
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; }
quelle
Ich bin auf diesen speziellen Fehler gestoßen, weil ich dieselben SqlParameter-Objekte als Teil einer SqlParameter-Auflistung zum mehrmaligen Aufrufen einer Prozedur verwendet habe. Der Grund für diesen Fehler ist meiner Meinung nach, dass die SqlParameter-Objekte einer bestimmten SqlParameter-Sammlung zugeordnet sind und Sie nicht dieselben SqlParameter-Objekte verwenden können, um eine neue SqlParameter-Sammlung zu erstellen.
Also stattdessen:
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); */
Mach das:
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);
quelle
Ich bin auf diese Ausnahme gestoßen, weil ich ein Parameterobjekt nicht instanziieren konnte. Ich dachte, es würde sich über zwei Verfahren mit gleichnamigen Parametern beschweren. Es wurde beschwert, dass derselbe Parameter zweimal hinzugefügt wurde.
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.
quelle
Geben
Sie mir eine SQL Server gespeicherte Prozedur von C # ausgeführt wird, wenn ich dieses Problem auftreten:
Ursache
Ich habe 3 Parameter an meine gespeicherte Prozedur übergeben. Ich habe das hinzugefügt
param = command.CreateParameter();
insgesamt nur einmal. Ich hätte diese Zeile für jeden Parameter hinzufügen sollen, es bedeutet insgesamt dreimal.
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);
Lösung
Hinzufügen der folgenden Codezeile für jeden Parameter
param = command.CreateParameter();
quelle
So habe ich es gemacht!
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)); }
quelle
Wenn Sie EntityFramework verwenden
Ich hatte auch die gleiche Ausnahme. In meinem Fall habe ich SQL über einen EntityFramework-DBContext aufgerufen. Das Folgende ist mein Code und wie ich ihn repariert habe.
Defekter Code
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 }
Hinweis: Der obige Aufruf von gibt
SqlQuery<T>()
tatsächlich a zurückDbRawSqlQuery<T>
, was implementiert wirdIEnumerable
Warum löst der Aufruf von .Count () die Ausnahme aus?
Ich habe SQL Profiler nicht zur Bestätigung gestartet, aber ich vermute, dass dies einen
.Count()
weiteren Aufruf von SQL Server auslöst und intern dasselbeSQLCommand
Objekt wiederverwendet und versucht, die doppelten Parameter erneut hinzuzufügen.Lösung / Arbeitscode
Ich fügte einen Zähler in meinem hinzu
foreach
, damit ich eine Zeilenzählung durchführen konnte, ohne anrufen zu müssen.Count()
int rowCount = 0; foreach(var row in rows) { rowCount++ // do something } if (rowCount == 0) { // tell user there are no rows }
Trotzdem
Mein Projekt verwendet wahrscheinlich eine alte Version von EF. Die neuere Version hat diesen internen Fehler möglicherweise behoben, indem die Parameter gelöscht oder das
SqlCommand
Objekt entsorgt wurden .Oder vielleicht gibt es explizite Anweisungen, die Entwickler anweisen,
.Count()
nach dem Iterieren von a nicht aufzurufenDbRawSqlQuery
, und ich codiere es falsch.quelle