2012-03-26 10 views
3

In meinem Testprojekt habe ich eine statische Klasse namens FixtureSetup, mit der ich meine Integrationstestdaten für die Validierung einrichten kann.Ist meine SqlCommand-Variable überlastet? Wurde es müde?

Ich benutze dieselbe SqlCommand- und SqlParameter-Variable (nicht das Objekt selbst) innerhalb dieser Klasse, wiederhole immer wieder dieselben Variablenreferenzen und weise jedes Mal neue SqlCommand- und SqlParameter-Objekte zu. Meine Verbindung selbst wird einmal erstellt und an die Methoden übergeben, die das Setup durchführen. Daher verwendet jedes Setup eine eigene eindeutige Verbindungsreferenz, und während dasselbe conn mehrmals verwendet wird, ist es immer in einer linearen Sequenz.

In einer solchen Methode stieß ich auf eine sehr merkwürdige Situation, in der meine SqlCommand-Variable einfach müde geworden zu sein scheint.

 cmd = new SqlCommand("INSERT INTO Subscription (User_ID, Name, Active) VALUES (@User_ID, @Name, @Active)", conn); 
     parameter = new SqlParameter("@User_ID", TestUserID); cmd.Parameters.Add(parameter); 
     parameter = new SqlParameter("@Name", "TestSubscription"); cmd.Parameters.Add(parameter); 
     parameter = new SqlParameter("@Active", true); cmd.Parameters.Add(parameter); 
     cmd.ExecuteNonQuery(); 

     cmd = new SqlCommand("SELECT Subscription_ID FROM [Subscription] WHERE Name = 'TestSubscription'", conn); 
     parameter = new SqlParameter("@User_ID", TestUserID); 
     cmd.Parameters.Add(parameter); 
     using (dr = cmd.ExecuteReader()) 
     { 
      while (dr.Read()) 
      { 
       TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID")); 
      } 
     } 

     cmd = new SqlCommand("INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (@Subscription_ID, @Company_ID)", conn); 
     parameter = new SqlParameter("@Subscription_ID", TestSubscriptionID); cmd.Parameters.Add(parameter); 
     parameter = new SqlParameter("@Company_ID", KnownCompanyId); cmd.Parameters.Add(parameter); 
     cmd.ExecuteNonQuery(); 

In der oben in der letzten Zeile gezeigt, das gleiche tun ich in Dutzenden von anderen Orten buchstäblich getan haben (Einsatzdaten, die ID-Spalte lesen und erfassen), erhalte ich folgendes:

Setup: System.InvalidOperationException: ExecuteNonQuery erfordert eine offene und verfügbare Verbindung. Der aktuelle Status der Verbindung ist geschlossen. bei System.Data.SqlClient.SqlConnection.GetOpenConnection (String-Methode)

ABER - ersetzen cmd mit neuen Variablen myCmd, und alles funktioniert swimmingly!

 SqlCommand myCmd; 
     myCmd = new SqlCommand("INSERT INTO Subscription (User_ID, Name, Active) VALUES (@User_ID, @Name, @Active)", conn); 
     parameter = new SqlParameter("@User_ID", TestUserID); myCmd.Parameters.Add(parameter); 
     parameter = new SqlParameter("@Name", "TestSubscription"); myCmd.Parameters.Add(parameter); 
     parameter = new SqlParameter("@Active", true); myCmd.Parameters.Add(parameter); 
     myCmd.ExecuteNonQuery(); 

     myCmd = new SqlCommand("SELECT Subscription_ID FROM [Subscription] WHERE Name = 'TestSubscription'", conn); 
     parameter = new SqlParameter("@User_ID", TestUserID); 
     myCmd.Parameters.Add(parameter); 
     using (dr = myCmd.ExecuteReader()) 
     { 
      while (dr.Read()) 
      { 
       TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID")); 
      } 
     } 

     myCmd = new SqlCommand("INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (@Subscription_ID, @Company_ID)", conn); 
     parameter = new SqlParameter("@Subscription_ID", TestSubscriptionID); myCmd.Parameters.Add(parameter); 
     parameter = new SqlParameter("@Company_ID", KnownCompanyId); myCmd.Parameters.Add(parameter); 
     myCmd.ExecuteNonQuery(); 

Was zum Teufel geht hier vor? Wurde mein Befehl var einfach nur müde ???

Was mich an die "Lösung" erinnerte war, dass ich in meinem Tracing merkte, dass mein cmd.Parameters Block in meinem Block "lese die ID" nur EINEN Parameter enthielt, den zweiten, und als ich gezwungen wurde erste cmd.Parameters.Add Zeile erneut auszuführen, die Anzahl der Parameter in der Liste fiel auf 0. Das hat mich dazu veranlasst, eine Methode Ebene SqlCommand versuchen ... weil ich die verrückte Idee hatte, dass mein cmd müde war ... Stellen Sie sich vor mein Schock, als ich mich als richtig herausstellte!

Edit: Ich recycle keine Objekte hier - nur die Variable selbst (statische SqlCommand auf Klassenebene). Ich entschuldige mich für die frühere Verwirrung in meiner Formulierung der Frage.

+0

Ja, das habe ich gerade selbst bemerkt. Festsetzung. –

+1

@marc_s: Sie sind nur in der Größe reduziert. Machen Sie ein 'Bild anzeigen' rechtsklicken Sie auf sie und sie sind lesbar. –

+0

Zu spät - bereits mit Codeschnipsel ersetzt –

Antwort

2

Big Edit:

Von: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.close.aspx

müssen Sie explizit Aufruf der Close-Methode, wenn Sie sind durch die SqlDataReader mit Hilfe des entsprechenden SqlConnection für andere Zweck zu verwenden.

die Close-Methode füllt die Werte für die Ausgangsparameter, Rückkehr Werte und RecordsAffected, die Zeit erhöhen, die es dauert, einen SqlDataReader zu schließen, die verwendet wurde, eine große oder komplexe Abfrage zu verarbeiten. Wenn die Rückgabewerte und die Anzahl der von einer Abfrage betroffenen Datensätze nicht signifikant sind, kann die Zeit zum Schließen des SqlDataReader reduziert werden, indem vor dem Aufrufen der Close-Methode die Cancel-Methode des zugehörigen SqlCommand-Objekts aufgerufen wird.

so versuchen:

using (dr = cmd.ExecuteReader()) 
{ 
    while (dr.Read()) 
    { 
     TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID")); 
    } 
    dr.Close(); 
} 
+0

Ich übergebe die Verbindung. Es ist die SqlCommand Objektreferenz, die ich recycle. Ich verstehe nicht, wie sich deine Antwort verhält ... –

+0

Aha! Jetzt hilft diese Verbindung, das Problem zu kristallisieren. Ich schätze, es dient mir dazu, in meinem Testprojekt faul mit den ADO-Objekten zu sein. –

+0

Nein ... es klickt immer noch nicht - denn wie Paul in den Kommentaren der Frage gezeigt hat, verwende ich nur Variablen, nicht die Objekte selbst. –

3

Verwenden Sie einen Befehl pro Abfrage und rufen Sie dispose (oder noch besser, wrap in einer using-Anweisung). Sie möchten die ado.net-Komponenten nicht "wiederverwenden".

+0

Warum nicht? cmd ist nichts anderes als eine statische Variablenreferenz. In jeder "Wiederverwendung" gebe ich explizit an: cmd = new SqlCommand - Und ich verwende es in Dutzenden und Dutzenden anderer Wiederverwendungen in derselben Klasse. –

+0

Das Problem ist nicht der Befehl, es ist die ** Verbindung **. Lesen Sie die Fehlermeldung (und meine Antwort). –

+0

Ich habe deine Antwort gelesen, und ich habe den Fehler gelesen. Ich habe auch den Fehler und die Verbindung zum Zeitpunkt des Problems untersucht. Die Verbindung meldet sich selbst in einem gültigen, offenen Zustand und der Wechsel von der Verwendung von statischem cmd zu der Methode local myCmd unter Verwendung der * same * Verbindung behebt das Problem, so dass ich immer noch nicht sehe, wie es die * Verbindung * ist ist das Problem? –

2

Sie sicher, dass Sie nicht die DataReader auf CommandBehavior.CloseConnection gesetzt haben, da Sie erwähnt, dass Sie erneut mit der Verbindung für Ihren Test Initialisierung.

Auch dann, wenn die DataReader Ressourcen nehmen, so nutzt Entsorgen

+0

Guter Gedanke, aber ich habe das überprüft. Ich habe sogar versucht, dem DataReader explizit eine eigene Verbindung zu geben ... das machte keinen Unterschied. –

0

Haben Sie nach jedem wirklich ein neues Verbindungsobjekt machen müssen versuchen tun?

myCmd = new SqlCommand(...) 
//your code 
myCmd = new SqlCommand(...) 
//etc 

Sie können nur sagen:

myCmd.CommandText = "INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (@Subscription_ID, @Company_ID)"; 

, so dass Sie Ihren Befehl Objekt wiederverwenden kann. Zusätzlich können Sie Ihre Parameter auch nach jedem Aufruf zurücksetzen. Rufen Sie einfach myCmd.Parameters.Clear().

Auch, stellen Sie sicher, dass Sie Ihre SqlCommand in einer using Anweisung wickeln, so dass sie ordnungsgemäß bereinigt werden.

using (SqlCommand cmd = new SqlCommand()) 
{ 
    cmd.CommandText = "some proc" 
    cmd.Connection = conn; 
    //set params, get data, etc 
    cmd.CommandText = "another proc" 
    cmd.Parameters.Clear(); 
    //set params, get date, etc. 
} 
+0

Nun ja, ich kann das tun, aber das beantwortet nicht wirklich, warum die existierende SqlCommand cmd-Variable, die wiederholt zurückgesetzt wird, nach Dutzenden von erfolgreichen Anwendungen nicht mehr funktioniert. –

+0

@TheEvilGreebo: Probieren Sie es aus und sehen Sie, ob es noch passiert. –

+0

Nicht notwendig, ich habe gezeigt, dass ich es bereits zur Arbeit gebracht habe. Die Frage ist nicht "Wie repariere ich es?" aber "warum funktioniert es nicht wie es ist?" –