Laden...

MySQL Where mit Wert aus WPF übergeben

Erstellt von Sascha87 vor 2 Jahren Letzter Beitrag vor 2 Jahren 564 Views
S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren
MySQL Where mit Wert aus WPF übergeben

Verwendetes Datenbanksystem: MySQL / MariaDB

Hallo zusammen,
ich bin aktuell dabei mein komplettes Script auf das MVVM Pattern umzuschreiben.
Es geht um dieses Projekt: Link

Die erste ComboBox fülle ich erfolgreich mit Werten aus der Datenbank per Databinding.


<ComboBox Name ="listFach" ItemsSource="{Binding Faecher}" SelectedIndex="0">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock x:Name="FachID" Text="{Binding Path=FachID}" />
                                <TextBlock Text=" - " />
                                <TextBlock Text="{Binding Path=FachName}" />
                            </StackPanel>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>

Die FachID lasse ich mir aktuell anzeigen, soll später, wenn möglich, nicht Sichtbar sein.

Diese genannte FachID möchte ich wieder zurück übergeben, da ich den Wert für eine Select Abfrage benötige.


class Jahrgaenge : ObservableCollection <Jahrgang>
    {
        private static Jahrgaenge _jahrgaenge = new Jahrgaenge();

        private Jahrgaenge() { }

        public static Jahrgaenge Load()
        {
            _jahrgaenge.Clear();
            using (var sqlConnection = new MySqlConnection(Properties.Settings.Default.DBConnectionString))
            {
                sqlConnection.Open();
                //Daten aus der Tabelle lesen
                

                string query = "select * from jahrgang a inner join faecherJahrgang fj on a.id = fj.jahrgangID where fj.faecherID = fachID";

                MySqlCommand cmd = new MySqlCommand(query, sqlConnection);
                cmd.Parameters.AddWithValue("@fachID", FachID.Text);

                MySqlDataReader reader = cmd.ExecuteReader();

                //Daten in fachtable füllen
                while (reader.Read())
                {
                    string jahrgangID = reader.GetString("id");
                    string jahrgang = reader.GetString("jahrgang");

                    _jahrgaenge.Add(new Jahrgang(jahrgangID, jahrgang));
                }
                reader.Close();
                sqlConnection.Close();
            }
            return _jahrgaenge;
        }
    }

Der erkennt nicht den Verweis auf die Textbox "FachID" aus der XAML Datei. Daher sagt er mir:

Fehlermeldung:
Der name "FachID" ist im aktuellen Kontext nicht vorhanden.

Ich wollte nicht zu viel Code posten. Sollte etwas fehlen, reiche ich es gerne nach.

Vielen lieben Dank und einen schönen Sonntag noch!
Grüße
Sascha

C
2.121 Beiträge seit 2010
vor 2 Jahren

In der Klasse Jahrgaenge gibt es nichts das FachID heißt.
Du brauchst an dieser Stelle die Instanz des WPF Formulars, denn die kennt das Element namens FachID.

Noch schöner wäre es wenn du den Text aus FachID als Parameter in die Load() Methode übergibst.
Dann muss diese Methode nicht mehr wissen woher dieser Wert stammt. Einer Methode die Daten lädt sollte das nämlich egal sein können.

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

Das hier übergebe ich an das WPF Formular. Die fachID kommt vor.


public static Faecher Load()
        {
            _faecher.Clear();
                 using (var sqlConnection = new MySqlConnection(Properties.Settings.Default.DBConnectionString))
                {
                    sqlConnection.Open();
                    //Daten aus der Tabelle lesen
                    string query = "select * from faecher order by fach asc";
                    MySqlCommand cmd = new MySqlCommand(query, sqlConnection);
                    
                    MySqlDataReader reader = cmd.ExecuteReader();

                    //Daten in fachtable füllen
                    while (reader.Read())
                    {
                        string fachID = reader.GetString("id");
                        string fach = reader.GetString("fach");

                        _faecher.Add(new Fach(fachID, fach));
                    }
                    reader.Close();
                    sqlConnection.Close();
            }
            return _faecher;
        }

309 Beiträge seit 2020
vor 2 Jahren

Der erkennt nicht den Verweis auf die Textbox "FachID" aus der XAML Datei. Daher sagt er mir:> Fehlermeldung:
Der name "FachID" ist im aktuellen Kontext nicht vorhanden.

Genau so macht man es mit MVVM nicht.

Es fehlen die relevanten Codeteile. Wie sieht dein ViewModel aus bzw. hast du überhaupt eins?
[Artikel] MVVM und DataBinding

C
2.121 Beiträge seit 2010
vor 2 Jahren

Das hier übergebe ich an das WPF Formular.

Was? Du zeigst eine Methode, die nichts übergeben bekommt.

Die fachID kommt vor.

Da kommt eine Variable namens fachID vor. Aber nicht FachID.Text so wie in deinem ersten Beitrag gezeigt.

Ein Zusammenhang deines zweiten Beitrags mit dem ersten ist nicht erkennbar.

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

Ok, einmal von vorn.

Ich habe folgende Klassen:

Fach.cs


public class Fach
    {
        public Fach(string fachID, string fach)
        {
            FachID = fachID;
            FachName = fach;
        }

        //Anzeige Fächer
        public string FachID { get; set; }
        public string FachName { get; set; }

        public override string ToString()
        {
            return $"FachID:{FachID},Fach:{FachName}";
        }

        internal void Add(Fach fach)
        {
            throw new NotImplementedException();
        }
    }

Faecher.cs


public class Faecher : ObservableCollection <Fach>
    {
        private static Faecher _faecher = new Faecher();
        private Faecher() { }
        public static Faecher Load()
        {
            _faecher.Clear();
                 using (var sqlConnection = new MySqlConnection(Properties.Settings.Default.DBConnectionString))
                {
                    sqlConnection.Open();
                    //Daten aus der Tabelle lesen
                    string query = "select * from faecher order by fach asc";
                    MySqlCommand cmd = new MySqlCommand(query, sqlConnection);
                    
                    MySqlDataReader reader = cmd.ExecuteReader();

                    //Daten in fachtable füllen
                    while (reader.Read())
                    {
                        string fachID = reader.GetString("id");
                        string fach = reader.GetString("fach");

                        _faecher.Add(new Fach(fachID, fach));
                    }
                    reader.Close();
                    sqlConnection.Close();
            }
            return _faecher;
        }
    }

ViewModel.cs


class ViewModel : NotifyPropertyBase
    {
        public Faecher Faecher { get; set; }
        public Jahrgaenge Jahrgaenge { get; set; }

        public ICommand RefreshCommand { get; set; }

        public ViewModel()
        {
            Faecher = Faecher.Load();
            Jahrgaenge = Jahrgaenge.Load();
            RefreshCommand = new DelegateCommand(OnRefresh, OnCanRefresh);
        }

        private bool OnCanRefresh(object parameter)
        {
            return NetworkInterface.GetIsNetworkAvailable();
        }

        private void OnRefresh(object parameter)
        {
            Faecher = Faecher.Load();
            Jahrgaenge = Jahrgaenge.Load();
        }
    }

Die Übergabe an das WPF damit funktioniert super. Die Fächer (Fächer-Name) und die Fach ID wird übergeben und angezeigt. Jetzt möchte ich, dass die ID, nach der Auswahl der ComboBox an die
Jahrgaenge.cs übergeben wird mit folgendem Inhalt:


class Jahrgaenge : ObservableCollection <Jahrgang>
    {
        private static Jahrgaenge _jahrgaenge = new Jahrgaenge();

        private Jahrgaenge() { }

          public static Jahrgaenge Load()
        {
            _jahrgaenge.Clear();
            using (var sqlConnection = new MySqlConnection(Properties.Settings.Default.DBConnectionString))
            {
                sqlConnection.Open();
                //Daten aus der Tabelle lesen
                

                string query = "select * from jahrgang a inner join faecherJahrgang fj on a.id = fj.jahrgangID where fj.faecherID = @fachID";

                MySqlCommand cmd = new MySqlCommand(query, sqlConnection);

                cmd.Parameters.AddWithValue("@fachID", listFach.SelectedValuePath);

                MySqlDataReader reader = cmd.ExecuteReader();

                //Daten in fachtable füllen
                while (reader.Read())
                {
                    string jahrgangID = reader.GetString("id");
                    string jahrgang = reader.GetString("jahrgang");

                    _jahrgaenge.Add(new Jahrgang(jahrgangID, jahrgang));
                }
                reader.Close();
                sqlConnection.Close();
            }
            return _jahrgaenge;
        }
    }

Ich hoffe, es bringt ein bischen mehr Licht ins dunkle. Ich bin "leider" noch Anfänger und probiere mich am MVVM Pattern.

C
2.121 Beiträge seit 2010
vor 2 Jahren

Das nicht gefundene Element heißt jetzt anders, aber das Problem ist dasselbe. Nämlich die Klasse Jahrgaenge weiß nicht was listFach sein soll, denn das Object das so heißt gehört zu einer anderen Klasse.
Lösung: Gib den zu suchenden Wert als Parameter in die Methode Load() hinein.

4.931 Beiträge seit 2008
vor 2 Jahren

Dann mußt du auch die Auswahl der ComboBox (SelectedValue) als ViewModel-Eigenschaft erstellen (und per XAML binden) und diese dann in den weiteren ViewModel-Methoden verwenden.

PS: Warum sind die Load-Methoden als static definiert und benutzen jeweils statische Membervariablen?

Außerdem solltest du dir auch mal [Artikel] Drei-Schichten-Architektur durchlesen: Datenbankabfragen sollten nicht im ViewModel (Logik) durchgeführt werden, sondern in einer eigenen Datenzugriffsklasse (-schicht).

Und sowohl MySqlCommand als auch MySqlDataReader sollten per using-Anweisung umschlossen sein - dann benötigst du auch kein manuelles Close() mehr.

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

@TH69

Dann mußt du auch die Auswahl der ComboBox (SelectedValue) als ViewModel-Eigenschaft erstellen (und per XAML binden) und diese dann in den weiteren ViewModel-Methoden verwenden.

Hättest du ein Beispiel- Code für mich?

Außerdem solltest du dir auch mal [Artikel] Drei-Schichten-Architektur durchlesen: Datenbankabfragen sollten nicht im ViewModel (Logik) durchgeführt werden, sondern in einer eigenen Datenzugriffsklasse (-schicht).

Die Datenbankabfrage findet nicht in der ViewModel statt.

Und sowohl MySqlCommand als auch MySqlDataReader sollten per using-Anweisung umschlossen sein - dann benötigst du auch kein manuelles Close() mehr.

Wird gemacht.

@Chilic

Lösung: Gib den zu suchenden Wert als Parameter in die Methode Load() hinein.

Ich frage nur ungern, aber kannst du mir ein Beispiel geben?

Ich bin euch unendlich Dankbar für eure Hilfe!

16.806 Beiträge seit 2008
vor 2 Jahren

Die Datenbankabfrage findet nicht in der ViewModel statt.

Also wenn ich mir Deinen Code anschau: doch 🙂

Auch verwendest Du zB paar Anti-Pattern bzw Code, der nicht testbar ist.
Den DB Code solltest Du komplett aus dem ViewModel auslagern und nicht über statische Methoden umsetzen; dafür sind statische Methoden nicht gedacht (und machen es eben untestbar).
Dafür gibts etablierte Pattern wie zB Repository Pattern (Docu ist sehr ausführlich, daher sieht es nach mehr aus, als es tatsächlich ist).

Ich frage nur ungern, aber kannst du mir ein Beispiel geben?


Load(listFach.SelectedValuePath)

4.931 Beiträge seit 2008
vor 2 Jahren

Nur, damit du, Sascha87, es nicht falsch verstehst. Der obige Code von Abt ist zwar syntaktisch jetzt richtig, aber benutzt direkt eine UI-Komponente (listFach.SelectedValuePath), und das ist eben nicht MVVM-konform.

Als Beispiel: Step by Step WPF Data Binding with Comboboxes ("Databinding ComboBox Example #1": Eigenschaft ColorString im VM-Code sowie das zugehörige Binding im vorherigen XAML-Code).

Und diese (entsprechend für deinen Code umbenannte) Eigenschaft verwendest du dann als Parameter für die Load(...)-Methode.

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

Super, vielen lieben Dank an euch! Ich bin zumindest ein Schritt weiter. Auch was das Verständnis angeht. Aber leider gibt es direkte das nächste Problem.

Aktuell gebe ich alle Werte aus und werden angezeigt. Ich habe an den ListBoxen/ComboBoxen SelectedIndex="0". Dadurch werden die ersten Werte angezeigt. Sobald ich das Fach wechsel, bekomme ich die Meldung

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

"value" war "null".

Zum Verständnis habe ich ein Bild angehängt. Die Frage ist, wie bekomme ich es hin, dass er die Nachfolgenden ComboBoxen/ListBoxen aktualisiert bzw. den Index auf 0 setzt?

meine ViewModel.cs:


class ViewModel : NotifyPropertyBase
    {

        public Faecher Faecher { get; set; }
        public Jahrgaenge Jahrgaenge { get; set; }
        public UVorhabens UVorhabens { get; set; }
        public Kompetenzen Kompetenzen { get; set; }
        public Foerdermaßnahmen Foerdermaßnahmen { get; set; }
        public ICommand RefreshCommand { get; set; }
        public Fach SelectedFachID { get; set; }


        Fach _currentFach = null;
        public Fach CurrentFach
        {
            get { return _currentFach; }
            set
            {
                Jahrgaenge = Jahrgaenge.Load(value.FachID);
                OnPropertyChanged(ref _currentFach, value);
            }
        }

        Jahrgang _currentJahrgang = null;
        public Jahrgang CurrentJahrgang
        {
            get { return _currentJahrgang; }
            set
            {
                UVorhabens = UVorhabens.Load(CurrentFach.FachID, value.JahrgangID);
                OnPropertyChanged(ref _currentJahrgang, value);
            }
        }

        Unterrichtsvorhaben _currentUVorhaben = null;
        public Unterrichtsvorhaben CurrentUVorhaben
        {
            get { return _currentUVorhaben; }
            set
            {
                Kompetenzen = Kompetenzen.Load(CurrentJahrgang.JahrgangID, value.UvID);
                OnPropertyChanged(ref _currentUVorhaben, value);
            }
        }

        Foerdermaßnahme _currentFoerdermaßnahme = null;
        public Foerdermaßnahme CurrentFoerdermaßnahme
        {
            get { return _currentFoerdermaßnahme; }
            set
            {
                Foerdermaßnahmen = Foerdermaßnahmen.Load(CurrentJahrgang.JahrgangID, CurrentUVorhaben.UvID);
                OnPropertyChanged(ref _currentFoerdermaßnahme, value);
            }
        }

        public ViewModel()
        {
            Faecher = Faecher.Load();
            RefreshCommand = new DelegateCommand(OnRefresh, OnCanRefresh);
        }

        private bool OnCanRefresh(object parameter)
        {
            return NetworkInterface.GetIsNetworkAvailable();
        }

        private void OnRefresh(object parameter)
        {
            Faecher = Faecher.Load();
            Jahrgaenge = Jahrgaenge.Load(CurrentFach.FachID);
            UVorhabens = UVorhabens.Load(CurrentFach.FachID, CurrentJahrgang.JahrgangID);
            Kompetenzen = Kompetenzen.Load(CurrentUVorhaben.UvID, CurrentJahrgang.JahrgangID);
            Foerdermaßnahmen = Foerdermaßnahmen.Load(CurrentUVorhaben.UvID, CurrentJahrgang.JahrgangID);
        }
    }

Es tut mir Leid, dass ich euch öfters Frage. Ich google auch vorher, wenn ich einen Ansatz habe. Aber irgendwie funktioniert kein Beispiel, dass ich gefunden habe.

C
55 Beiträge seit 2020
vor 2 Jahren

Es gibt da sowas das nennt sich Debugger, damit kannst deinen Code zur laufzeit Prüfen und sehen welches Object, welchen Wert gerade hat um das einfach auszudrücken. Desweiteren bekommst du bei einer Execption auch meistens die Info in welcher Zeit der Fehler auftaucht. Vermutlich taucht der Fehler bei den Objecten auf, die du mit null initiallisierst hast, was mich ehrlich gesagt auch nicht wundern wüde.

F
10.010 Beiträge seit 2004
vor 2 Jahren

Überlege doch mal logisch.

Wenn sich eine Combobox ändert, ändert sich auch SelectedIndex und u.a. SelectedValue.
Die bindest du auf ein Property in deinem ViewModel und in dem Setter kannst du dann alles machen was du willst.
Nur dran denken, bau dir keine endlosschleifen.