3 Stimmen

MySql "SET @variable" wirft fatalen Fehler in C#-Code

Ich erhalte einen schwerwiegenden Fehler bei der Verwendung einer ExecuteNonQuery(). Ich bin neu in der Arbeit mit Sql und als solche im nicht sicher, warum diese SET Befehl ein Problem verursacht.

Ich verwende MySql 5.5.17, und ich verwende C# .Net Framework 3.5 in der VS2010 IDE.

Exception Message = Schwerwiegender Fehler bei der Befehlsausführung. Die Fehlerzeile = SET @oldguid = 250006;

Der Inhalt der Sql-Datei, die ich verwende, sieht wie folgt aus, ohne die "Kommentare", die ich entfernt habe:

DELETE FROM `disables` WHERE `sourceType`=0 AND `entry`=61904;
INSERT INTO `disables` (`sourceType`, `entry`, `flags`, `comment`) VALUES(0, 61904, 8, 'Magma Totem TEST - can crash client by spawning too many totems');
SET @oldguid = 250006;
SET @newguid = 202602;
UPDATE `creature` SET `guid`=@newguid WHERE `guid`=@oldguid;
UPDATE `creature_addon` SET `guid`=@newguid, `path_id`=@newguid*100 WHERE `guid`=@oldguid;
UPDATE `waypoint_data` SET `id`=@newguid*100 WHERE `id`=@oldguid*100;
UPDATE `areatrigger_teleport` SET `target_orientation`=2.255664 WHERE `id`=4386;
UPDATE `gameobject` SET `spawnMask`=3 WHERE `guid`=22674;

die guid Spalte ist ein unsigned int(10)

Der C#-Code, mit dem ich diese .sql-Datei verarbeite, lautet wie folgt:

filename = lstBox_SqlFiles.SelectedItem.ToString();
mysql = new MySqlConnection(connection + Database);
string line;

OpenDatabase();
using (StreamReader reader = new StreamReader(filename))
{
  StringBuilder parsedLine = new StringBuilder();
  int count = 0;
  while ((line = reader.ReadLine()) != null)
  {
    if (line.Length > 0 && !line.StartsWith("--"))
    {
      if (line.EndsWith(";"))
      {
        if (parsedLine.Length > 0)
          line = parsedLine.ToString() + line;
        try
        {
          count++;
          lbl_LineQuery.Text = line;
          lbl_QueryCount.Text = String.Format("Count: {0}", count);

          MySqlCommand cmd = new MySqlCommand(line, mysql);
          cmd.ExecuteNonQuery();
        }
        catch (MySqlException ex)
        {
          string msg = String.Format("Source FileName: SqlUpdater.cs\nSql FileName: {0}\nError Line: {1}\nException Message: {2}\n", filename, line, ex.Message);
          MessageBox.Show("cmd.ExecuteNonQuery() Error!\n" + msg, "MySql Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
          return;
        }
        sbClear(parsedLine);
      }
      else
      {
        parsedLine.Append(line);
        continue;
      }
    }
    else
    continue;
  }
}

Kann jemand ein Problem mit meinem Code erkennen? Muss ich die Zeichenkette "SET @var" besonders manipulieren?

Jede Hilfe ist willkommen Vielen Dank im Voraus

Straßenmeisterei

  • Bearbeiten * als Randbemerkung sollte ich darauf hinweisen, dass, wenn ich ein Sql-Management-Programm wie SQLYog verwenden es die gleiche Sql-Datei ohne Problem verarbeitet, so im der Annahme, dass das Problem irgendwo in der Manipulation der Zeichenfolge in meinem C#-Code ist.

8voto

Paul Punkte 387

Hinzufügen. ;Allow User Variables=True in die Verbindungszeichenfolge einfügen.

4voto

Ben Punkte 389

Dieses Problem habe ich entdeckt, als ich die MySql.Data.dll von 1.0 auf 6.9 aktualisiert habe. Zum Beispiel:

MYSQL:

SET @foo = 'foo'

Nachricht: Schwerwiegender Fehler bei der Befehlsausführung aufgetreten.

Ich vermute, dass MySql.Data versucht, @foo als vorbereitete Anweisungsvariable zu interpretieren, und will, dass ich sie ausfülle. Unter Androhung des Todes. Die Lösung scheint daher zu sein, MySql.Data mitzuteilen, dass es sich um ein wörtliches "@" und nicht um ein Token handelt.

Es mag andere Möglichkeiten geben, aber die pragmatische Lösung, die ich gefunden habe, ist, den Variablennamen in Anführungszeichen zu setzen:

SET @`foo` = 'foo'
SELECT @`foo`

Ich hoffe, das hilft.

3voto

GianT971 Punkte 4175

Eigentlich glaube ich nicht, dass Sie diese Zeile übergehen können SET @oldguid = 250006; in ein mysqlcommand-Objekt (ich weiß es nicht genau). Was Sie tun sollten, ist, dass Ihr Programm diese Werte in eine lokale Variable schreibt und dann den Parameter in Ihren Aktualisierungsabfragen ersetzt.

Finden Sie einen Weg, Ihren Code und diesen zu mischen:

        // This line should be outside the While loop
        Dictionary<string, string> variables = new Dictionary<string, string>();

        if (line.Trim().ToUpper().StartsWith("SET"))
        {
            List<string> commandLine;
            commandLine = line.Trim(' ',';').Split().Distinct().ToList();
            commandLine.Remove(string.Empty);

            // After next line, the 'variables' dictionary contains the name 
            // of the variable you want to set, and its value
            variables[commandLine[1]] = commandLine[3];

            // ...
            // Do something here (try catch, chatever, but NO MySqlCommand)
            // ...
        }
        else
        {
            // If the line contains any of the variables you previously set,
            // i will be the index of this variable, -1 otherwise
            int i = line.ContainsAnyOfAt(variables.Keys);
            while(i>=0)
            {
                // Here we replace the parameter by its value, for example:
                // UPDATE `creature` SET `guid`=@newguid WHERE `guid`=@oldguid;
                // becomes (after all loops):
                // UPDATE `creature` SET `guid`=202602 WHERE `guid`=250006;
                line = line.Replace(variables.Keys.ElementAt(i), variables.Values.ElementAt(i));
                i = line.ContainsAnyOfAt(variables.Keys,i+1);
            }

            // ...
            // This is where you should put the code of MySqlCommand
            // ...
        }

Und hier ist die Erweiterungsmethode ContainsAnyOfAt :

        public static int ContainsAnyOfAt<T>(this global::System.String source, IEnumerable<T> values, int startIndex = 0)
        {
            if (source == null) throw new ArgumentNullException("source");
            for (int i = startIndex ; i < values.Count(); i++)
            {
                if (source.Contains(values.ElementAt(i).ToString()))
                {
                    return i;
                }
            }
            return -1;
        }

Bitte probieren Sie es aus und geben Sie Feedback. Grüße

2voto

p.campbell Punkte 94960

Der Code funktioniert im Wesentlichen wie folgt:

  • alle SQL-Befehle aus einer Datei in eine Sammlung von Zeichenketten laden.
  • diese Sammlung von oben nach unten durchlaufen:
    • eine neue MySqlCommand (und schafft damit einen eigenen Geltungsbereich!)
  • MySQL gibt in der 3. Codezeile einen Fehler aus, da die Variable @oldguid wurde nicht erklärt.

Wenn Sie in der Lage sein wollen, Variablen zu verwenden, benutzen Sie ein MySqlCommand-Objekt, und rufen Sie ExecNonQuery() einmal.

Am besten ist es, eine gespeicherte Prozedur zu erstellen. Vielleicht wird es 2 Parameter für Ihre "guid"/integer Werte benötigen.

Der Grund dafür, dass es in SQLYog funktioniert, ist, dass all diese Anweisungen im selben Kontext und nicht in verschiedenen Sitzungen ausgeführt werden.

2voto

Gjermund Dahl Punkte 1376

Der Vorschlag hat bei mir funktioniert.

Ich habe eine ziemlich komplexe Prozedur in meiner MySQL-Datenbank, die ich nicht in meinem C#-Code enthalten möchte. Hier ist, wie ich es tun:

//Open connection
string conString = "server=myserver; Uid=myusername; database=myscheme; Pwd=mypwd";
MySqlConnection con = new MySqlConnection(conString);

try
{
    //Set first variable, @START
    string sql = "SET @START = '" + date1 + "'";
    MySqlScript cmd = new MySqlScript(con, sql);
    cmd.Connection.Open();
    cmd.Execute();

    //Set second variable, @END
    sql = "SET @END = '" + date2 + "'";
    cmd = new MySqlScript(con, sql);
    cmd.Execute();

    //Send procedure call
    MySqlDataAdapter MyDA = new MySqlDataAdapter();
    sql = "call MY_DB_PROCEDURE";
    MyDA.SelectCommand = new MySqlCommand(sql, con);

    //From here you can process the DB response as you want
    //For instance, display the results in a datagridview
    DataTable table = new DataTable();
    MyDA.Fill(table);

    BindingSource bSource = new BindingSource();
    bSource.DataSource = table;

    //"dataGridView1" is already placed in the active form
    dataGridView1.DataSource = bSource;
    dataGridView1.Visible = true;

}
catch (Exception e)
{
    //Error handling procedure
}
finally
{
    if (con.State == ConnectionState.Open)
    {
        con.Dispose(); // return connection to the pool
    }
}

EDITAR

Eine elegantere Lösung besteht darin, der Verbindungszeichenfolge "Allow User Variables=True" hinzuzufügen. Beispiel wie folgt:

//Open connection
string conString = "server=myserver; Uid=myusername; database=myscheme; Pwd=mypwd; Allow User Variables=True";
MySqlConnection con = new MySqlConnection(conString);

try
{
    //Open connection
    con.Open();

    //Set first variable, @START
    //Set second variable, @END
    //Send procedure call
    string sql = "SET @START = '" + date1 + "';" +
                 "SET @END   = '" + date2 + "';" +
                 "CALL MY_DB_PROCEDURE";

    MySqlDataAdapter MyDA = new MySqlDataAdapter();
    MyDA.SelectCommand = new MySqlCommand(sql, con);        

    //From here you can process the DB response as you like
    //...

}
catch (Exception e)
{
    //Error handling procedure
}
finally
{
    if (con.State == ConnectionState.Open)
    {
        con.Dispose(); // return connection to the pool
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X