Laden...

Wo baue ich am besten die Datenbankverbindung auf?

Erstellt von tobi45f vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.367 Views
T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren
Wo baue ich am besten die Datenbankverbindung auf?

Hallo zusammen,

ich habe ein Problem und weiß nicht so ganz, wo/wie ich da weiter komme.

Kurz zur Struktur und dann, wo mein Problem liegt 😉

Ich habe eine Klasse für die Datenbankverbindungen


public class SqlLiteRepository<T> : ISqlRepository<T> where T : class
    {
        private readonly string _tableName;
        private readonly string _conString;

        public SqlLiteRepository(string filename, string tableName)
        {
            _tableName = tableName;
            _conString = "URI=file: " + filename;
        }
public async Task<IList<T>> GetAllAsync(){...}
public async Task UpdateAsync(T t, string whereKey) {...}
....

Eine Berechnungs-Klasse, diese erhält durch die Oberfläche den Pfad zur Datenbank, liest relevante Daten und verarbeitet diese. Anschließend können unterschiedliche Analysen vom Nutzer angestoßen werden.
Nach den Berechnungen werden die Ergebnisse in einem Datagrid dargestellt. Der Nutzer soll hier einige Fehler anpassen können (DropDown). Ändert er dieses Feld, so soll die Änderung in die Datenbank übernommen werden.



<Window.Resources>        
        <ObjectDataProvider x:Key="dataFromEnum"
       MethodName="GetValues" ObjectType="{x:Type System:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:ProtEnum"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>

...

<DataGridTemplateColumn Header=" test " CanUserResize="False">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}" 
                                                    SelectedItem ="{Binding ComboBoxItem1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                                    Width="70" />
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>

Die Itemssource greift auf die Klasse zurück und das Dropdown-Feld wird wie folgt befüllt:


public Enum ComboBoxItem1
        {
            get
            {
                if (Kabelende_1.Flag_state == 0) return ProtEnum.Trennm;
                else
                {
                    if (Kabelende_1.ProtLocation == null) return ProtEnum.Vollst;
                    else
                    {
                        return (ProtEnum)Kabelende_1.ProtLocation.Protchare_id.Value;
                    }
                }

            }
            set
            {
                ??                
            }
        }

Nun stehe ich vor dem Problem, dass ich nicht weiß, wie ich mein set vernünftig einbinde. Möglich und einfach wäre es ja, den Datenbankpfad in meine Klasse zu übergeben und dort dann einfach im set den Code zu schreiben. Ich bin mir aber sicher, dass das nicht grade elegant gelöst ist und es besser Lösungen gibt. Der Pfad ist nur in der Berechnungsklasse vorhanden und alle SQL Verbindungen laufen derzeit dort.

Ich denke, dass ich mich mit meiner Struktur hier verzettele.

Der einzige Wert, der vom Nutzer geändert werden kann, ist eben in einer DropDown-Liste. Von daher habe ich das Thema INotifyOnPropertyChanged als nicht relevant angesehen? Also wenn ich eine Klasse habe und sich ein Wert ändert, dann sollte man INotifyOnPropertyChanged implementieren. Aber was genau passiert da im Hintergrund? (also ich rufe im Set PropertyChanged auf aber was bringt das?) Von all den Artikeln, die ich gelesen habe und den Videos die ich gesehen habe, wird immer nur gezeigt, wie der Code aussieht. Aber was genau ist der tiefere Sinn und Zweck?
Wenn ich über INotifyOnPropertyChanged erkenne, dass sich ein Wert ändert, dann wäre mein Anwendungsfall der, dass ich dabei dann die Datenbankverbindung aufbaue und den Wert auch in der DB ändere. Wenn ich in meiner Klasse den PropertyChangedEventHandler implementiere und im Set diesen aufrufe, dann hab ich nichts gewonnen!? Nach wie vor bin ich in der Klasse, wo Pfad zur Datenbank nicht existiert und auch nichts zu suchen hat!?

Brauche ich irgendwelche Klassen "dazwischen" - bisher habe ich das Thema MVVM nicht beachtet, da ich der Ansicht war, dass ich es nicht brauche. Brauche ich es doch? 😄

Habt ihr Tipps, in welche Richtung ich gehen muss, damit es vernünftig gelöst wird?
Viele Grüße Tobias!

edit: oh ich sehe grade, dass ich das falsche Unterforum ausgewählt habe! Sorry! Ich wollte eigentlich in Basistechnologien und allgemeine .NET-Klassen posten!

16.834 Beiträge seit 2008
vor 3 Jahren

Der WPF ist der richtige Bereich, da es hier letzten Endes um WPF mit MVVM geht.

Die Verbindung kannst Du im simpelsten Fall im ViewModel erzeugen; in der Property jedoch auf keinen Fall.
Auch solltest Du nicht unbedingt im Setter eine Datenbank-Aktion ausführen, denn diese sollten asynchron erfolgen, was im Set direkt nicht möglich ist.

Brauche ich irgendwelche Klassen "dazwischen" - bisher habe ich das Thema MVVM nicht beachtet, da ich der Ansicht war, dass ich es nicht brauche. Brauche ich es doch? 😄

Ohne MVVM wirst Du in WPF nich weit kommen; dir werden die kleinsten Dinge Probleme machen, für die Du Workaround um Workaround bauen musst, weil das gesamte Konzept von WPF auf die Zusammenarbeit mit MVVM ausgerichtet ist.
MVVM nicht anzuwenden wird sehr wahrscheinlich ein Schuss in Ofen werden; Du wirst keinen Spaß ohne MVVM haben.

PS: das Produkt heisst Sqlite nicht SqlLite

5.658 Beiträge seit 2006
vor 3 Jahren

Aber was genau passiert da im Hintergrund? (also ich rufe im Set PropertyChanged auf aber was bringt das?) Von all den Artikeln, die ich gelesen habe und den Videos die ich gesehen habe, wird immer nur gezeigt, wie der Code aussieht. Aber was genau ist der tiefere Sinn und Zweck?

Siehe dazu [Artikel] MVVM und DataBinding:

Das ViewModel löst die jeweiligen Events aus den INotifyPropertyChanged- und INotifyCollectionChanged-Schnittstellen aus, damit die Anzeige automatisch aktualisiert werden kann.

Die View muß ja schließlich wissen, wann sie aktualisiert werden muß. Das geht nicht über Magie, sondern über ein Event. Und dieses Event muß halt ausgelöst werden, wenn sich ein Wert ändert, sonst kann die View den neuen Wert nicht anzeigen.

Weeks of programming can save you hours of planning

T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren

Hi,

danke für die Einschätzung! Ich verstand es so, dass man MVVM anwendet, wenn es darum geht, sein Programm auf unterschiedlichen Plattformen zu verwenden, da der View einfach ausgetauscht werden kann.

Tatsächlich hat mir der MVVM Artikel hier sehr beim Verständnis geholfen, mehr als die Erklärungen, die ich zuvor auf anderen Seiten gefunden hatte.

Wenn ich mir den Artikel angucke, dann ist es ja zunächst essenziell das Programm in drei Klassen aufzuteilen - Model, View, Viewmodel halt. Was inhaltlich passiert passiert mit dem DataBinding habe ich tatsächlich auch schon so gemacht, nur dass bei mir die ViewModel-Schicht nicht klar abgetrennt ist und zum Teil vielleicht in der Model Schicht steckt (glaube ich 😉) und eben INotifyPropertyChanged noch nicht implementiert hat.

Ich werde mal versuchen das Projekt neu aufzusetzen und meine bisherigen Klassen zuzuordnen und anzupassen.
Ich hoffe es ist in Ordnung mein Anliegen zu beschreiben und nachzufragen, ob mein Vorgehen und mein Verständnis für MVVM korrekt ist!?

Mein Programm soll eine lokale Datenbank einlesen, die Daten (elektrische Netzdaten) analysieren und die Ergebnisse ausgeben und gewisse Änderungen sollen durchgeführt werden können auf der Oberfläche.
Bisher war mein Aufbau wie folgt:

MainWindow.xaml - View - bisher alle Elemente drin wie Buttons, ListBox, DataGrid - über Binding verknüpft.
MainWindow.xaml.cs - (?!) Die Hintergrundaktionen wie FileOpen (Instanzierung des Netzmodells), Einlesen der SQLite, Click Events, Start von Analysefunktionen, setzen der ItemsSource für die Anzeigeelemente, wenn die Analyse erfolgreich war. (Ich würde behaupten, dass das relativ nah an das ViewModel-Konzept geht?)
DB.cs - eine Klasse für die relevanten Datenbanktabellen. Enthalten sind einige List<> für die Objekte. Für die relevanten Tabellen habe ich Projektionen erstellt.
Ortsnetz.cs - eine Klasse für "das gesamte Netz" (die Datenbank mit dem gesamten Netz enthält mehrere, elektrisch isolierte Bereiche). Einige Methoden werden auf das gesamte Netz angewendet. Ein Path-Finding-Algorithmus selektiert mit die einzelnen isoliertn Bereiche und instanziiert mir diese mit deren elektrischen Daten -->
Inselnetz.cs - ist eine Instanz eines Teilbereichs, mit natürlich anderen Methoden
Pinzipiell stellen die Klassen DB, Ortsnetz und Inselnetz meine Model-Schicht dar? Sehe ich das richtig?

Ich habe in meinem View einmal eine ListBox, in der mir meine Instanzen der Inselnetze dargestellt werden, um diese eben zu selektieren. Daneben dann das DataGrid zur Darstellung der Ergebnisse des Path Findings bzw. weiterer Ergebnisse, wenn berechnet worden.
Brauche ich hier dann zwei ViewModels, da ich von Ortsnetz->View und von Inselnetz -> View gehe oder braucht man je View ein ViewModel und es wird zusammengefahren (fast so, wie es derzeit schon ist, nur mit anständiger Benennung 😄)? Oder ist das egal? Theoretisch sollte ja dasselbe rauskommen?

Meine Analysen befinden sich in eigenen Klassen, damit die Klasse Inselnetz nicht so vollgestopft wird. Läuft die Darstellung dieser Ergebnisse über das ViewModel von Inselnetz oder brauche ich hier ein eigenes?

Kann man das grob so machen oder ist meine Struktur/Verständnis auf erster Ebene schon schlecht, falls man das mit der rudimentären Beschreibung beurteilen kann? 😉

Viele Grüße Tobias

16.834 Beiträge seit 2008
vor 3 Jahren

Wenn ich mir den Artikel angucke, dann ist es ja zunächst essenziell das Programm in drei Klassen aufzuteilen - Model, View, Viewmodel halt.

Dann schau ihn Dir nochmal an.

Nicht das ganze Programm, sondern strukturell jeweils Deine Views etc.
Dem gesamten Programm (u.a. der Datenbank, der Logik) ist völlig egal welchen UI Pattern Du verwendest.

Ich hoffe es ist in Ordnung mein Anliegen zu beschreiben und nachzufragen, ob mein Vorgehen und mein Verständnis für MVVM korrekt ist!?

Beachte halt [Hinweis] Wie poste ich richtig?; Endlosthreads sind nicht gerne gesehen.
Meist wird hier Zeug wiederholt, das ohnehin schon dutzende Male im Forum besprochen wurde.
Dein Thread / das Forum ist nicht zum begeleitenden Lernen sondern für konkrete Probleme gedacht.

4.939 Beiträge seit 2008
vor 3 Jahren

Hallo tobi45f,

generell gilt [Artikel] Drei-Schichten-Architektur für die Unterteilung des Gesamtprogramms und MVVM wird dann nur innerhalb der UI-Schicht angewendet. Und so gehört also der Datenbankzugriff in die untere Datenzugriffsschicht (auch hier gibt es wieder generelle Patterns, z.B. Repository und UnitOfWork).

T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren

Hallo zusammen,

ich habe mein Projekt jetzt auf MVVM umgestellt. Quasi neu aufgesetzt und die VMs für "dazwischen" hinzugefügt und den Code-Behind entfernt. Es erleichtert die Steuerung des UI tatsächlich ungemein - allerdings ist es erstmal echt kompliziert mit der "neuen" Struktur.

Auch solltest Du nicht unbedingt im Setter eine Datenbank-Aktion ausführen, denn diese sollten asynchron erfolgen, was im Set direkt nicht möglich ist.

Auch jetzt mit MVVM erkenne ich ja am set, dass die Änderung vorgenommen worden ist. Dass die Datenbankänderung asynchron erledigt wird ist klar. Dass es im set nicht geht auch. Deswegen ja auch mein Post hier. Klar könnte man die Änderung von so wenigen Werten auch synchron machen. Richtig ist das aber nicht.
In einem Beispielprojekt habe ich gesehen, dass die ObservableCollection das CollectionChanged Event aboniert hat. Ich nehme an, die Variante wäre das Mittel der Wahl?

16.834 Beiträge seit 2008
vor 3 Jahren

Klar könnte man die Änderung von so wenigen Werten auch synchron machen.

Sowas hat nichts mit der Menge zutun, sondern mit IO. Das sind Faktoren, die Du als Programmierer nicht beeinflussen kannst, sondern Dein Code darauf vorbereiten musst.

Du hast keine Chance, dass die UI hier nicht blockiert, wenn die Verbindung zum Speichern langsam oder weg ist.
Dann sagt Deine UI sie reagiert nicht und der User hat schlechte Laune.

Das Vorgehen fällt unter Defensives Programmieren

In einem Beispielprojekt habe ich gesehen, dass die ObservableCollection das CollectionChanged Event aboniert hat. Ich nehme an, die Variante wäre das Mittel der Wahl?

Wie der Name schon sagt bezieht sich das Event darauf, dass sich die Collection ändert.
Es ist aber nicht (standardmäßig) so, dass der Event gefeuert wird, wenn sich die Property eines Elements ändert.

T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren

Hallo,

Magst du mir/irgendjemand dann verraten, wie bzw. wo man es dann macht, statt nur zu sagen, was man nicht macht?

Entweder ich habe die falschen Suchbegriffe oder ich lese die falschen Artikel, aber ich finde nichts, was mir hilft. Im Gegenteil, ich finde viele Beispiele, die synchrone Methoden für die Datenbankinteraktionen aufbauen.

Folgender Post beschreibt mein Problem und hat es auch genau so umgesetzt.


//VM
public Enum ProtComboBox
        {
            get{...}
            set
            {
                var t = Db_repo.ProtEnumValueChange(_leitung.Terminal, (ProtEnum)value);
                
                OnPropertyChanged("Fuse");
                OnPropertyChanged("Typ");
                OnPropertyChanged("Switchstate");
            }
        }


//Model
public async Task ProtEnumValueChange(Terminal protLocationTerminal, ProtEnum value)
{
// Die Combobox repräsentiert einen Wert. Mit Änderung des Wertes werden dann die entsprechenden Werte an den drei Objekten geändert
// nachdem die Objekte angepasst wurden, werden die async. Datenbankinteraktion durchgeführt
}

16.834 Beiträge seit 2008
vor 3 Jahren

Die Verbindung kannst Du im simpelsten Fall im ViewModel erzeugen; in der Property jedoch auf keinen Fall.
Auch solltest Du nicht unbedingt im Setter eine Datenbank-Aktion ausführen, denn diese sollten asynchron erfolgen, was im Set direkt nicht möglich ist.

Was genau als Info fehlt Dir noch?

Folgender Post beschreibt mein Problem und hat es auch genau so umgesetzt.

Ich sehe keine akzeptierte Antwort in dem Thread. Das einzige Code-Beispiel hat eklatante Fehler im Umgang mit async.

Nur eine Antwort ist inhaltlich auf dem korrekten Weg

You can't. INotifyPropertyChanged doesn't support async calls. You need to do a hack, or rethink you strategy.

INotifyPropertyChanged is not intended for async actions. Its goal is to enable a class notify the UI that its data has changed. The UI works in a dedicated thread, so cross-thread operations must be avoided.

Dein eigener Link zeigt Dir also: mach keine DB-Aktionen im Setter, dafür ist er nicht da. Für Dich ganz arg wichtig: rethink you strategy.
Aber Du hast ja auch den Link zu [Artikel] MVVM und DataBinding bekommen, in dem steht, dass man das über die Commands macht.

Daher ist mir nun Deine Kritik, dass Dir hier keiner sagen würde wie es richtig geht, unklar.

T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren

Wenn ich die Lösung aus dem Post akzeptiert hätte, dann hätte ich nicht noch mal geantwortet. Dass die Lösung nicht korrekt ist, ist mir klar. Interessanterweise kann ich lesen und habe auch den Hinweis "rethink your strategy" gelesen.
Und ja, auch die Verwendung von Commands ist mir bekannt und habe ich auch so umgsetzt. Nur besitzt die ComboBox keine Möglichkeit Commands (direkt) zu nutzen. Wenn das das Mittel der Wahl ist, dann weiß ich schon mal mehr. Die vergangenen Tage ist mir das Thema auch schon vor die Füße gefallen. Allerdings hab ich auch da keine funktionierende Lösung gefunden.

16.834 Beiträge seit 2008
vor 3 Jahren

Ich mag mich täuschen, weil ich in WPF nicht mehr 100% fit bin, aber Du kannst doch einfach eine Interaction auf die ComboBox setzen, die auf einen SelectionChanged Trigger hört und dann einen Command ausführen...? 🤔
Du hast dann zwar kein Parameter am Command des selektierten Items, aber das bekommst ja über das Binding.

T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren

Ja, damit geht es dann. Etwas uncool, dass erst das Set und anschließend das Event aufgerufen wird und somit im Set die Veränderung geschrieben wird und anschließend dann die Datenbank verändert werden soll. Die Änderung kann ich also nicht schreiben und muss somit pauschale Änderungen vornehmen. Halb so wild.

Wie mit MVVM auf ComboBox SelectionChanged-Event reagieren?

Danke!

16.834 Beiträge seit 2008
vor 3 Jahren

Was ist "das Set?" Sprichst Du vom Binding (also den Setter), oder was?
Wenn ja, dann ist das doch die korrekte Reihenfolge.

T
tobi45f Themenstarter:in
59 Beiträge seit 2017
vor 3 Jahren

Genau, der Setter. Natürlich ist das die korrekte Reihenfolge, allerdings geht die Info verloren, welcher Wert sich geändert hat (das Dropdown Element ist beinhaltet eine Änderung an mehreren Properties)
Beispiel:
Jetzt habe ich x=1, y=1
über die Dropdownauswahl kommt der neue Wert
x=5, y=1, somit wird über das Set x=5 gesetzt. y bleibt aber gleich.
Anschließend wird das Event ausgeführt. Theoretisch müsste ich in der Datenbank nur die 5 anpassen. Da ich aber nicht weiß, welcher Wert geändert wurde (außer ich würde es mir irgendwo zwischenspeichern), update ich dann beide Werte statt nur dem geänderten Wert.

16.834 Beiträge seit 2008
vor 3 Jahren

Du solltest bei Deinen Beiträgen beachten, dass wir als Leser Deinen Code und Deine Anwendung nicht vor Augen haben.
Keiner hier weiß, wie Deine Datenbankstruktur aussieht und von "welchen beiden Werten" Du sprichst.

Ich zumindest kann null nachvollziehen, wo Dein Update-Problem nun ist und was die beiden Werte damit zutun haben.
Es ist kurios, dass Du wissen musst, welcher Wert aktuell in der DB steht, damit Du aktualisieren kannst.
Lokal kannst Du keinen Wert halten, weil das ja ansonsten eine potentielle Race Condition für einen DB-Wert darstellt.