Laden...

MySQL Select mit einem Insert innerhalb einer if -Anweisung gepaart.

Erstellt von Sascha87 vor 2 Jahren Letzter Beitrag vor 2 Jahren 539 Views
S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren
MySQL Select mit einem Insert innerhalb einer if -Anweisung gepaart.

Verwendetes Datenbanksystem: MySQL/MariaDB

Hallo zusammen, ich habe erneut ein Problem bei einer Abfrage.
Ich wusste nicht genau wie ich mein Problem im Title beschreiben sollte. Ich hoffe der ist ok so.

Im ersten Teil lese ich ein Passwort aus und vergleiche es mit dem eingegebenen Passwort (Das Eingabefeld ist eine TextBox, wird aber noch geändert. Ist nur zum testen).

Wenn der Vergleich zutrifft (Bis hier hin funktioniert es) führt er den Teil unter "try" aus. Allerdings macht er keinen DB Eintrag. Jemand einen Tipp wie ich an mögliche Fehlerquellen komme oder sieht auf anhieb was falsch ist? (Bin Anfänger und das ganze ist zum testen)


private void BtnUvEintrag_Click(object sender, RoutedEventArgs e)
        {
            //Passwort auslesen
            MySqlDataAdapter mysqlDataAdapter = new MySqlDataAdapter();
            MySqlCommand cmd;
            MySqlDataReader reader;

            cmd = new MySqlCommand("select passwort from passwort where fachID = @fachID", sqlConnection);
            sqlConnection.Open();
            cmd.Parameters.AddWithValue("@fachID", listFach.SelectedValue);
            cmd.Parameters.AddWithValue("@uvInsert", sendUvorhaben.Text);
            cmd.Parameters.AddWithValue("@jahrgangID", listJahrgang.SelectedValue);

            reader = cmd.ExecuteReader();
            reader.Read();

            var passDB = reader["passwort"].ToString();

            if (listFach.SelectedValue != null && listJahrgang.SelectedValue != null && Passwort.Text == passDB)
            {
                
                try
                {
                    cmd = new MySqlCommand("insert into uvorhaben (uvorhaben) value (@uvInsert); insert into faecherJahrgangUvorhaben (fachID, jahrgangID, uvorhabenID) value (@fachID, @jahrgangID, LAST_INSERT_ID())", sqlConnection);
                    MessageBox.Show("Unterrichtsvorhaben wurde gespeichert.");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                finally
                {
                    if (sqlConnection != null) sqlConnection.Close();
                }

            }
            else
            {
                MessageBox.Show("Das Fach und der Jahrgang muss ausgewählt sein.\nEventuell ist auch das Passwort falsch");
            }
        }

Vielen Dank für eure Hilfe! Ich bin sehr Dankbar dafür!
Liebe Grüße
Sascha

16.806 Beiträge seit 2008
vor 2 Jahren

Also zunächst muss man Dich darauf hinweisen, dass das, was Du hier machst, in der EU verboten ist sofern Du das nicht für Dich privat oder als Übung einsetzt.
Das EU Gesetz verbietet aktiv das Speichern von Passwörtern im Klartext. Da der Einsatz hier unbekannt ist, einfach mal als Hinweis nehmen.

Die Ursache Deines Fehler ist wahrscheinlich, dass Du beim neuen Command die Parameter nicht setzt.
Ebenso musst Du den Command ausführen, was Du nicht tust. Du erstellst nur den Command, und machst damit dann nichts.

PS: in einer realen Welt wird man UI-Code, Logik und Datenbank nicht miteinander mischen.
[Artikel] Drei-Schichten-Architektur
In der Software Welt gibts dafür Best Practises wie verschiedene Pattern, zB. Repository Pattern.

PPS: schau Dir using() an.
Da packt man üblicherweise die Commands und die Connection rein.

Siehe das Tutorial von ADO.NET:
Erstellen einer einfachen Datenanwendung mit ADO.NET - Visual Studio

S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren

Also zunächst muss man Dich darauf hinweisen, dass das, was Du hier machst, in der EU verboten ist sofern Du das nicht für Dich privat oder als Übung einsetzt.

Das Passwort wird später wieder verschlüsselt. Grundsätzlich bastel ich das Script zur zum Lernen.
Deine Links schaue ich mir weiterhin an. Ich möchte gerne alle Funktionen erst einmal fertigstellen.
Anschließend, den Hinweis hattest du mir schon einmal gegeben, werde ich alles in die "Drei-Schichten-Architektur" umwandeln. Dazu wollte ich das jetzige erst einmal verstehen.
Zusätzlich muss ich mir noch DataBindings anschauen.

Vielen lieben Dank!

T
2.219 Beiträge seit 2008
vor 2 Jahren

Passwörter werden nicht verchlüsselt sondern gehasht!
Schau dir dazu mal die best Practices an, damit du dies mit der möglichst gängigen Praxis machst.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.806 Beiträge seit 2008
vor 2 Jahren

Die noch bessere Idee ist eine Identity / Credentials Verwaltung nicht selbst zu programmieren; und ob das nun gehasht oder verschlüsselt wird ändert an der Grundsache nichts.
Ob ein Passwort verschlüsselt oder gehasht wird, das kommt auf den Anwendungsfall an. Bei einem Loginsystem sollte man immer gesalzene Hashes erzeugen und entsprechend die Hashes und nicht die Klarwerte vergleichen => Salt (Kryptologie)

Ansonsten bitte On-Topic bleiben, T-Virus 🙂

T
2.219 Beiträge seit 2008
vor 2 Jahren

Um wieder zum Topic zurück zu kommen, du prüfst beim Read des DataReader nicht ob er false liefert.
In dem Fall würde dein aktueller Code beim Zugriff auf das Passwort Feld knallen, da es keine Einträge geben kann

Ebenfalls schließt du u.U. auch die verbindung nicht richtig, hier hatte Abt schon den Tipp mit using gegeben.
Dadurch würdest du deine Verbindungen während der Laufzeit der Anwendung ggf. aufbrauchen und kannst dann gar nicht mehr mit der DB kommunizieren.
Hier solltest du aber vor dem Abfragen gegen die DB prüfen ob die entsprechenden Felder/Parameter gefüllt und gültig sind.
Dann kannst du dir das aufbauen der Verbindung sparen und direkt eine Meldung ausgeben.

Die erste Abfrage erschließt sich mir nicht.
Warum hast du eine Tabelle mit dem Namen passwort und fragst nur die fachId ab?
Wäre eine Benutzer Tabelle nicht sinnvoller?
Hier kenne ich das Schema deiner Datenbank nicht, deshalb verwundert mich die Abfrage.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren

Ich übergebe die FachID an die Tabelle (wurde vorher in einer ListBox ausgewählt).
Dadurch suche ich das richtige Passwort raus (Die Passwörter sind nicht an Benutzer sondern an die Fächer gebunden) und vergleiche anschließend
das vom Benutzer eingegebene Passwort mit dem Passwort aus der Datenbank (die entsprechende if).


if (listFach.SelectedValue != null && listJahrgang.SelectedValue != null && Passwort.Text == passDB)

Using habe ich bisher immer genutzt. Ich wusste nur nicht, dass ich die verschachteln kann. Dennoch scheint es (bei mir) nicht ganz zu funktionieren.
Ich verstehe den eigentlichen Aufbau bis jetzt noch nicht.

16.806 Beiträge seit 2008
vor 2 Jahren

Mach Dir das Leben leichter und struktier Deinen Code, misch nicht alles.
Ich kann mir nicht vorstellen, dass Du selbst Deinen Code überblickst; zB setzt Du oben im Code Parameter, die gar nicht in dem SQL Command benötigt werden.
Dafür vergisst Du sie unten zu setzen, wo sie nötig wären.

Modularisier Deinen Code in die einzelnen Verantwortlichkeiten, dann weißt Du genau, was was macht und was Du für Inhalte brauchst.
Das ist die Vorstufe für [Artikel] Drei-Schichten-Architektur

Kann zB so aussehen (nur im Editor getippt, kompiliert bestimmt nicht aber Du siehst ne Richtung)


private void BtnUvEintrag_Click(object sender, RoutedEventArgs e)
{
    using (IDbConnection connection = GetConnection())
    {
        bool isValidPassword = IsValidPassword(connection, listFach.SelectedValue, Passwort.Text);

        if (isValidPassword)
        {
            // hier der Rest
        }
    }
 
}

public IDbConnection GetConnection()
{
    // erzeuge SQL Connection
    return sqlConnection;
}

public bool IsValidPassword(IDbConnection connection, int fachId, string password)
{
   // prüf nicht das Passwort in Deinem Code, sondern schaue nach einem Treffer in der DB
    var sql = "select count(*) from passwort where fachID = @fachID AND password = @password";

    bool isValid;
    using (IDbCommand cmd = cmd = new MySqlCommand(sql, connection))
    {
        cmd.Parameters.AddWithValue("@fachID", fachId);
        cmd.Parameters.AddWithValue("@password", password);

        int count = Convert.ToInt32(cmd.ExecuteScalar());

        isValid = count == 1;
    }

    return isValid;
}

D
30 Beiträge seit 2021
vor 2 Jahren

Hallo,

Ich würde den Rat von Abt beherzigen - bau jetzt den Code im richtigen Pattern - sonst haste nachher ggf. das Problem, dass du in "alte" und somit ggf. falsche Muster zurückfällst.

Hast du denn mal Breakpoints gesetzt und geprüft ob listFach.SelectedValue != null && listJahrgang.SelectedValue != null wirklich true ist?

Eine Bedingung muss ja False sein, oder vorher eine Exception passieren, dass du dort nicht reinspringst.

Gruß,
D.

S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren

Guten Morgen zusammen,
der Code sieht wirklich übersichtlicher aus. Ich werde es mir ab jetzt vornehmen und alles überarbeiten.

@Abt, ich habe dein Beispiel gerade übernommen und abgeändert. Bevor ich in die Datenbank speichere gebe ich in der if Anweisung erst einmal eine MessageBox aus.

Was folgendes passiert, wenn ich kein Fach auswähle, bricht das Script einfach ab. Ich würde jetzt einfach eine if Anweisung zur Überprüfung machen (wie im Ursprungsscript von mir).

Jetzt würde mich folgender Satz von dscNRW interessieren:

Hast du denn mal Breakpoints gesetzt und geprüft ob listFach.SelectedValue != null && listJahrgang.SelectedValue != null wirklich true ist?

Überprüfe ich nicht mit der Abfrage ob es true ist?

Liebe Grüße und vielen lieben Dank!
Sascha

T
2.219 Beiträge seit 2008
vor 2 Jahren

Was dscNRW damit sagen willst ist, dass du per debugger mal schauen sollst ob die Werte, die du dort erwartest auch drin stehen.
Dazu brauchst du Breakpoints, damit der Debugger an der Stelle anhält und du die Variablen bzw. in dem Fall die Eigenschaften der Controls prüfen kannst.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

4.931 Beiträge seit 2008
vor 2 Jahren

Du solltest besser zwei verschiedene Abfragen bzgl. ausgewählter Werte und dem Passwort machen und dann auch verschiedene MessageBox-Texte ausgeben.

S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren

Ok, das mit den Breakpoints habe ich verstanden und ist eine super Hilfe. Ich wusste nicht das man so etwas in Studio einstellen kann.

Ich glaube, ich habe erneut einen Gedankenfehler.

Ich habe aktuell ein Problem mit folgender Zeile:


bool isValidPassword = IsValidPassword(connection, (int)listFach.SelectedValue, Passwort.Text);

Wenn ich kein Fach angebe, schmeißt er mir einen Fehler aus.Klar, enthält ja keinen Wert.

Fehlermeldung:
System.NullReferenceException: "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."

System.Windows.Controls.Primitives.Selector.SelectedValue.get hat null zurückgegeben.

Ich würde, mit meinem Wissensstand daher gehen und eine if Abfrage vorher machen, um zu prüfen, ob die Zeile ausgefüllt ist.


private void BtnUvEintrag_Click(object sender, RoutedEventArgs e)
        {
            using (MySqlConnection connection = GetConnection())
            {
                if (listFach.SelectedValue != null && listJahrgang.SelectedValue != null) 
                {

                    bool isValidPassword = IsValidPassword(connection, (int)listFach.SelectedValue, Passwort.Text);

                    if (isValidPassword)
                    {
                        MessageBox.Show("Das Unterrichtsvorhaben wurde erfolgreich angelegt.");
                    }
                    else
                    {
                        MessageBox.Show("Es wurde ein falsches Passwort angegeben!");
                    }

                }
                else
                {
                    MessageBox.Show("Folgende Angaben müssen vorher gemacht werden:\n- Fach\n- Jahrgang\n- Passwort");
                }

                
            }
        }

Da gibt es mit Sicherheit auch einen anderen Weg, oder?
Auch wieder in eine separate Funktion auslagern?

4.931 Beiträge seit 2008
vor 2 Jahren

Da es sich hierbei um eine reine UI-Methode handelt, ist es so schon i.O.
Du solltest allerdings die Zeile


using (MySqlConnection connection = GetConnection())

innerhalb des if-Blocks verschieben (so daß nicht auch im Fehlerfall unnötigerweise eine DB-Verbindung erzeugt wird).

T
2.219 Beiträge seit 2008
vor 2 Jahren

Der Fehler kommt, da du mit "(int)listFach.SelectedValue" eine Umwandlung des ausgewählten Wert zu int forcierst.
Da du den Wert aber nicht vorab auf null prüfst, knallt die Umwandlung.
Daher auch mein Rat von Gestern, doch erst zu prüfen ob die Werte stimmen und gültig sind.
Erst nach dem du die Eingaben validiert hast, solltest du die Abfrage gegen die Datenbank machen.
Dann wäre auch sichergestellt, dass du kein NULL oder ungültige Werte hast.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren

Ich hoffe, ich gehe euch nicht all zu sehr auf den Sack. Nach und nach verstehe ich es so langsam.

Die if Anweisung ist eine Möglichkeit, ich habe mir jetzt das "gegen Null" prüfen angeschaut.

Dazu habe ich folgende Methode erstellt:


public bool IsNullOrEmptyWertAusWPF(string wertAusWPF)
        {
            bool isempty;

            isempty =  wertAusWPF == null || wertAusWPF == string.Empty;
            
            return isempty;
        }

Wenn ich jetzt 2 verschiedene Typen habe, einmal int und einmal string, müsste ich die Methode genau danach ausrichten? Sprich 2 Methoden anlegen?

Das ganze im Event mit eingefügt:


private void BtnUvEintrag_Click(object sender, RoutedEventArgs e)
        {
            using (MySqlConnection connection = GetConnection())
            {
                
                    bool IsNullOrEmptyFach = IsNullOrEmptyWertAusWPF((string)listFach.SelectedValue);
                    bool IsNullOrEmptyJahrgang = IsNullOrEmptyWertAusWPF((string)listJahrgang.SelectedValue);
                    bool isValidPassword = IsValidPassword(connection, listFach.SelectedValue, Passwort.Text);

                    if (isValidPassword && IsNullOrEmptyFach && IsNullOrEmptyJahrgang)
                    {
                        MessageBox.Show("Das Unterrichtsvorhaben wurde erfolgreich angelegt.");
                    }
                    else
                    {
                        MessageBox.Show("Folgende Angaben müssen vorher gemacht werden:\n- Fach\n- Jahrgang\n- Passwort");
                    }
            }
        }

Der Fehler kommt, da du mit "(int)listFach.SelectedValue" eine Umwandlung des ausgewählten Wert zu int forcierst.

Auch, wenn es doof klingt, hat mir Studio gesagt, ich soll (int) eintragen da ein Objekt nicht in int konvertiert werden kann.
Als alternativ Vorschlag kam folgendes:


throw new NotImplementedException();

Wenn ich richtig verstehe, wird die Meldung damit nur unterdrückt?

4.931 Beiträge seit 2008
vor 2 Jahren

Damit machst du es nicht besser, da du jetzt eine explizite Konvertierung nach string vornimmst, ohne vorher zu testen, ob es auch wirklich ein string ist!
Beim expliziten Cast mußt du als Programmierer sicher sein, daß auch wirklich dieser Datentyp vorliegt, ansonsten besser darauf abfragen: [FAQ] Casten aber richtig: Boxing/Unboxing - () / is / as / Pattern Matching

PS: Es gibt schon die Methode String.IsNullOrEmpty.