2016-11-08 3 views
1

Bevor Sie diese Frage als ein Duplikat markieren, hier ist der schwierige Teil, den ich nicht verstehe. Dieser Fehler ist sporadisch, ich glaube, dass der Code korrekt ist und er funktioniert immer und ich behandle die möglichen Fehler mit einer if else-Bedingung innerhalb des Reader-Teils. Hier ist der Code:Code nicht synchronisiert werfen Index war außerhalb der Grenzen Array?

public static Tuple<int, string> GetIDAndString(string term) 
{ 
    try 
    { 
     using (SqlConnection con = GetConnection()) 
     using (cmd = new SqlCommand()) 
     using (myReader) 
     { 
      int ID = 0; 
      string status = string.Empty; 
      cmd.Connection = con; 
      con.Open(); 
      cmd.CommandText = @"SELECT t.TableID, t.Status 
           FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */ 
           WHERE t.Term = @term"; 
      cmd.Parameters.AddWithValue("@term", term); 

      myReader = cmd.ExecuteReader(); 
      while(myReader.Read()) 
      { 
       ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); 
       status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim(); 
      } 

      myReader.Close(); 

      return new Tuple<int, string>(ID, status); 
     } 
    } 
    catch (Exception) 
    { 
     throw; 
    } 
} 

Ich weiß, ich sollte eine Klasse anstelle eines Tuple werden, aber ich kann nicht, dass die vorhandenen Code ändern und wie Sie sehen können. Das Hauptproblem ist also, dass es auf dem Produktionsserver eine Index out of bounds array exception in dieser Methode gab, aber ich kann nicht identifizieren, was das Problem ist.

Auch wenn der Begriff in der Abfrage nicht gefunden wird, wird der myReader nicht eingegeben und ich gebe die ID = 0, status = string.Empty zurück. Manchmal, wenn ich Code debugge und an der develpment server arbeite, beginnt mein Code überall zu stürzen, zeigt mir Ausnahmen, wo Code getestet wird, und ich muss die Lösung erneut öffnen, um das zu vermeiden (ich habe keine Lösung dafür gefunden, nicht einmal Reinigung der Lösung).

Also ich hoffe, jemand hat Erfahrung mit so etwas in einer production server. Ich habe keine Spezifikationen für den Produktionsserver, daher weiß ich nichts über den Server.

+0

Erstellen Sie neue Datenleser teilen Sie es nicht! Das könnte dir Probleme bereiten! – mybirthname

+0

Stimmen Sie dem vorherigen Kommentar zu, entweder erstellen Sie eine neue Instanz oder synchronisieren Sie den Zugriff darauf. –

+0

@mybirthname, Ich bin neu in der Programmierung, Sie sagten, den DataReader nicht zu teilen. Sie meinen, keine Klasse zu verwenden, in der ich einen geschützten statischen SqlDataReader deklarierte und ihn dann in einem using-Block verwendete? –

Antwort

1

Zuerst brauchen Sie nicht den try/catch Block, Sie tun nichts damit. Danach teilen Sie nicht SqlDataReader in der Klasse, dies könnte Probleme bringen und wahrscheinlich kommt das Problem von diesem. Sie überschreiben ständig den Wert ID und Status. Wahrscheinlich ist eine gute Idee, Top 1 auf Ihrer Abfrage zu benennen und es durch mit korrektem Feld zu bestellen. Auch gibt es keine Notwendigkeit, Dispose() der SqlCommand, der Konstruktor von SqlCommand ruft SupressFinalization().

Warum kann dieses Problem auftreten: Stellen Sie sich vor, dass Ihre Abfrage 1000 Datensätze mit TableID und Status Spalte zurückgibt und Sie die While-Schleife eingeben. In diesem Moment geht ein anderer Benutzer in Ihre Anwendung und führt eine andere Methode aus, die die SqlDataReader überschreibt und 5 Datensätze mit nur einer Spalte zurückgibt. Bei der nächsten Iteration Ihrer while-Schleife erhalten Sie Ihre Ausnahme. Aus diesem Grund sollten Sie Ihre Leser niemals als static für die gesamte Klasse definieren. Statische Variablen werden von allen Anwendungsbenutzern gemeinsam genutzt.

public static Tuple<int, string> GetIDAndString(string term) 
{ 
    int ID = 0; 
    string status = string.Empty; 

    using (SqlConnection con = GetConnection()) 
    { 
     SqlCommand cmd = new SqlCommand(); 
     cmd.Connection = con; 
     con.Open(); 

     cmd.CommandText = @"SELECT t.TableID, t.Status 
          FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */ 
          WHERE t.Term = @term"; 
     cmd.Parameters.AddWithValue("@term", term); 


     using(SqlDataReader myReader = cmd.ExecuteReader()) 
     { 
      while(myReader.Read()) 
      { 
       ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); 
       status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim(); 
      } 

     } 

    } 

    return new Tuple<int, string>(ID, status); 
} 
+0

Tolle Erklärung! Ich gebe Ihnen die richtige Antwort, weil es die gleiche ist wie die andere, aber mit einem Beispiel meines Codes mit Best Practices. Nur eine andere Sache hoffe du kannst das für mich klären. Wenn ich 'protected static SqlTransaction' in der 'GetConnection()' Methode verwende, sollte ich die Änderungen wie der SqlReader innerhalb der Klasse vornehmen? Oder nur für den SqlReader? Danke –

+0

@JorgeF ja nicht statische Transaktion verwenden. Überprüfen Sie msdn-Artikel: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction(v=vs.110).aspx. – mybirthname

+0

Danke, ich mache diese Änderungen –

0

Dies passiert wahrscheinlich, wenn Sie ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); oder status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim(); tun, weil das Ergebnis nicht Ihren Erwartungen entspricht. Sie sollten die Protokollierung der Leserzeile hinzufügen, bevor Sie sie tatsächlich lesen, damit Sie das Problem genauer lokalisieren können.

0

using (myReader) erfasst den Wert, den der Leser zu dieser Zeit hatte und entsorgt diesen später. Es erinnert sich nicht an die Variable. Dies muss so sein, wie Sie an diesem Beispiel sehen können: using (Random() ? myReader : null). Es ist klar, dass die C# -Sprache diesen Ausdruck nicht zur Verfügungszeit erneut ausführt. Es läuft nur einmal.

Sie verfügen also über einen alten/anderen Leser.

Für den Fall, dass Sie Objekte zwischen Threads teilen (vielleicht mit statischen Variablen) ist dies trivialerweise eine Race-Bedingung. Tu das nicht. Verwenden Sie Einheimische. Es besteht kein Bedarf/Vorteil, statische Variablen hier zu verwenden.

Verwandte Themen