Programmeinstellungen vor dem Entladen speichern

  • WPF MVVM
  • .NET (FX) 4.5–4.8

Es gibt 30 Antworten in diesem Thema. Der letzte Beitrag () ist von siycah.

    Neu

    kafffee schrieb:

    Verzeih mir wenn ich mich täusche aber die Property ist doch Public?
    Nur die Variable zum Zwischenspeichern ist Private...


    Tatsache! Wer lesen kann, ist natürlich klar im Vorteil. Wegen der Einrückung im Post musste ich gerade aber auch noch ein zweites Mal schauen.
    Aber es stimmt auch, dass es eine Indexed-Property ist. VB ist mir nicht ganz geläufig und die Syntax ist (für mich) auch weniger verständlich als C#. Aber das wird schwer, das zu serialisieren.
    Am sinnigsten wäre es denke ich, wenn du deine _EqualiserLeft Property nochmal in eine eigene Eigenschaft lädst und mit deinem Indexer separat drauf zugreifst, oder deine Property direkt zu einem Array umänderst und dann direkt darauf zugreifst.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Neu

    Okay, also auf der Website von Newtonsoft heisst es:

    IEnumerable, Lists, and Arrays.NET lists (types that inherit from IEnumerable) and .NET arrays are converted to JSON arrays. Because JSON arrays only support a range of values and not properties, any additional properties and fields declared on .NET collections are not serialized. In situations where a type implements IEnumerable but a JSON array is not wanted, then the JsonObjectAttribute can be placed on the type to force it to be serialized as a JSON object instead.

    So wie ich das verstehe ist es wohl eingeschränkt möglich, aber ich kann mich auch täuschen:
    "any additional properties and fields declared on .NET collections are not serialized"

    Versteht jemand den englischen Text richtig? Was meinen die mit "additional"?

    siycah schrieb:

    Am sinnigsten wäre es denke ich, wenn du deine _EqualiserLeft Property nochmal in eine eigene Eigenschaft lädst und mit deinem Indexer separat drauf zugreifst, oder deine Property direkt zu einem Array umänderst und dann direkt darauf zugreifst.


    Kannst du mir vielleicht an einem kleinen Beispiel zeigen, wie du das meinst, ich verstehe es nicht ganz...

    Neu

    Die meinen glaub eine Auflistung, die noch mehr tut als nur auflisten.
    Kommt nur sehr selten vor.
    ZB IGrouping(Of T) ist eine Auflistung, die zusätzlich noch ein Key-Feld transportiert.
    ich verstehe den Satz so, dass der Serializer diesen Key nicht serialisiert.
    in anderen Worten: Für deinen Anwendungsfall nicht relevant.

    Neu

    Habe auch noch Folgendes gefunden:

    Public and non-public fields
    Newtonsoft.Json can serialize and deserialize fields as well as properties.

    In System.Text.Json, use the JsonSerializerOptions.IncludeFields global setting or the [JsonInclude] attribute to include public fields when serializing or deserializing. For an example, see Include fields.

    Das war hier:
    learn.microsoft.com/en-us/dotn…lic-and-non-public-fields

    Was meinen die mit "Fields"?

    So wie @ErfinderDesRades meint?

    Neu

    kafffee schrieb:


    IEnumerable, Lists, and Arrays.NET lists (types that inherit from IEnumerable) and .NET arrays are converted to JSON arrays. Because JSON arrays only support a range of values and not properties, any additional properties and fields declared on .NET collections are not serialized. In situations where a type implements IEnumerable but a JSON array is not wanted, then the JsonObjectAttribute can be placed on the type to force it to be serialized as a JSON object instead.

    So wie ich das verstehe ist es wohl eingeschränkt möglich, aber ich kann mich auch täuschen:
    "any additional properties and fields declared on .NET collections are not serialized"


    Muttersprachler eilt zur Hilfe!

    Da steht, dass Objekte, basierend auf IEnumerable<T>, List<T> und Arrays zu JSON Arrays serialisiert werden; was man schließlich auch erwartet.

    C#-Quellcode

    1. void SerialiseObjects() {
    2. var ex1 = new List<string> { "Element 1", "Element 2", "Element 3" };
    3. var ex2 = ex1.Select(x => $"${x}.ienumerable");
    4. var ex3 = new string[] { "Element Arr1", "Element Arr2", "Element Arr3" };
    5. var json1 = JsonConvert.SerializeObject(ex1);
    6. var json2 = JsonConvert.SerializeObject(ex2);
    7. var json3 = JsonConvert.SerializeObject(ex3);
    8. }


    Der Output wäre:

    Quellcode

    1. json1: [ "Element 1", "Element 2", "Element 3" ]
    2. json2: [ "Element 1.ienumerable", "Element 2.ienumerable", "Element 3.ienumerable" ]
    3. json3: [ "Element Arr1", "Element Arr2", "Element Arr3" ]


    Klar, ein Beispiel zu meinem Vorschlag kann ich dir auch geben. Aber in C# :D

    C#-Quellcode

    1. class MySettings {
    2. public short[] EqualizerLeft { get; set; } = { 0, 1, 2, 3 };
    3. public short this[int idx] {
    4. get => EqualizerLeft[idx];
    5. set {
    6. if (idx > short.MaxValue) { throw new IndexOutOfRangeException("zu groß!"); }
    7. else if (idx < short.MinValue) { throw new IndexOutOfRangeException("zu klein!"); }
    8. EqualizerLeft[idx] = (short)value;
    9. }
    10. }
    11. internal override string ToString() => JsonConvert.SerializeObject(this);
    12. }
    13. // Verwendung
    14. class User {
    15. void Foo() {
    16. var settings = new Settings();
    17. short myValue = settings[2];
    18. // myValue hat nun den Wert 2
    19. }
    20. }


    Das JSON dazu wäre:

    Quellcode

    1. {
    2. "EqualizerLeft": [ 0, 1, 2, 3 ]
    3. }
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Neu

    Eines solltest Du in jedem Fall beachten, wenn Du beim Beenden des Programmes Daten oder Einstellungen speicherst.
    Die heutigen PC-Systeme mit SSD sind mitunter so schnell, dass Windows beim Herunterfahren Deine Anwendung beendet, Deine Anwendung nun versucht noch letzte Daten zu schreiben, aber der PC schon ausgeschaltet wird, während Dein Programm noch schreibt.

    Dies passiert häufig, wenn man die my.settings verwendet.
    Dann ist das XML-File (das im Appdata-Ordner liegt) meist nicht komplett geschrieben und die Anwendung startet nicht mehr.

    Daher würde ich versuchen die Daten und Einstellungen während der Programmlaufzeit zu speichern, dann, wenn sich die Einstellungen oder Daten ändern.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Neu

    Daher würde ich versuchen die Daten und Einstellungen während der Programmlaufzeit zu speichern, dann, wenn sich die Einstellungen oder Daten ändern.


    Genau. Hierzu bin ich letztens bei folgendem Tool fündig geworden, beim Versuch den MS-Ranz (Settings) loszuwerden:
    github.com/aloneguid/config
    Es funktioniert auch mit DI Containern zusammen, was meine Hauptanforderung war. Wollte es nur mal fix hier einwerfen.
    An manchen Tagen gibt es zu allem Überfluss auch noch Ärger!

    Neu

    siycah schrieb:

    Muttersprachler eilt zur Hilfe!


    Haha :P , irgendwie hab ich mir schon gedacht, dass du englischsprachig bist. Daher ein kleiner Tipp:

    siycah schrieb:

    Leider bietet den Faden bisher wenig Quellcode

    In diesem Zusammenhang kannst du "thread" ruhig mit "Thread" übersetzen. "Faden" ist nur die wörtliche Übersetzung, wenn du das Utensil meinst, mit dem man Socken flickt... Ansonsten ist dein Deutsch perfekt.


    siycah schrieb:

    Am sinnigsten wäre es denke ich, wenn du deine _EqualiserLeft Property nochmal in eine eigene Eigenschaft lädst und mit deinem Indexer separat drauf zugreifst, oder deine Property direkt zu einem Array umänderst und dann direkt darauf zugreifst.

    Okay jetzt hab ichs verstanden. Habs so ausprobiert und es funktioniert :)

    dive26 schrieb:

    Dies passiert häufig, wenn man die my.settings verwendet.

    my.settings, was ist das? Ich benutze es nicht, zumindest nicht wissentlich

    dive26 schrieb:

    Daher würde ich versuchen die Daten und Einstellungen während der Programmlaufzeit zu speichern, dann, wenn sich die Einstellungen oder Daten ändern.

    Ich mache das so:
    Ich hab mir ein Interface im ViewModel erstellt:

    VB.NET-Quellcode

    1. Interface IClosing
    2. Function OnClosing() As Boolean
    3. End Interface


    Dann hab ich dieses in meinem ViewModel implementiert:

    VB.NET-Quellcode

    1. Public Function OnClosing() As Boolean
    2. Dim close As Boolean = True
    3. 'hier kann ich Aktionen einfügen, die vor dem Speichern ausgeführt werden sollen
    4. Return close 'und bei Bedarf kann ich hier mit "Return False" das Beenden abbrechen.
    5. End Function


    Dann noch das hier:

    VB.NET-Quellcode

    1. Private Sub Window_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs)
    2. Dim context As IClosing = TryCast(DataContext, IClosing)
    3. If context IsNot Nothing Then
    4. e.Cancel = Not context.OnClosing()
    5. End If
    6. End Sub


    Die Werte meiner Settings-Datenklasse schreibe ich jeweils in den Settern der an die Controls gebundenen Eigenschaften im ViewModel.

    Hatte zuerst vor, ReadOnly Properties zu machen, die sich erst beim Beenden des Programms die Daten in den Gettern holen, aber da hatte ich Probleme mit den richtigen Instanzen...

    Ich denke damit bin ich auf dem richtigen Pfad und die Settings werden rechtzeitg gespeichert bzw. das Programm wird ja erst beeendet, wenn OnClosing True zurückgibt... Ich bitte um Rückmeldung, wenn dem nicht so ist ?(

    Edit:
    @dive26
    Ich werd noch wahnsinnig, da denkt man man speichert mal kurz paar Einstellungen:
    Während ich manche Settings, z. B. den Text einer Textbox problemlos gespeichert bekomme, hab ich mit einer ObservableCollection(Of String), die an einer ComboBox im selben ViewModel hängt, schon wieder Probleme. Es wird ein Nullwert in das JSON geschrieben. Kann das an dem von dir geschilderten Problem liegen oder fällt das raus, weil ich my.settings nicht benutze?

    Während es bei manchen Einstellungen möglich wäre, direkt bei der Änderung in das JSON File zu schreiben, gibt es andere, wo das wenig sinnvoll wäre. Z. B. die eines Lautstärkereglers, was bei mir z. B. ein Single wäre, bzw. dann müsste / sollte ich ja zumindest noch prüfen, ob der Mausbutton denn dann wieder losgelassen wurde beim Verschieben des Sliders....

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()

    Neu

    kafffee schrieb:

    In diesem Zusammenhang kannst du "thread" ruhig mit "Thread" übersetzen. "Faden" ist nur die wörtliche Übersetzung, wenn du das Utensil meinst, mit dem man Socken flickt... Ansonsten ist dein Deutsch perfekt.


    Ich weiß ;)

    "Faden" nutze ich meist auf deutschen Foren, weil es mich immer wieder zum Schmunzeln bringt. Ich sage aber auch "Du Röhre" zu YouTube ;)

    Aber danke fürs Kompliment! Jahrelange Arbeit!

    kafffee schrieb:

    Okay jetzt hab ichs verstanden. Habs so ausprobiert und es funktioniert


    Das freut mich zu hören. Ich hoffe die Umstellung von C# zu VB war jetzt kein Riesenaufwand. Mit VB als Sprache konnte ich mich der Syntaxhalber nicht anfreunden. Irrwitzigerweise allerdings mit Lua und Bash...

    kafffee schrieb:

    Es wird ein Nullwert in das JSON geschrieben.


    Wenn in deinem JSON null steht, dann wurde der Parameter nicht korrekt serialisiert. Wenn du dein Programm debuggst, kannst du mit Debug.WriteLine() mal deinen JSON ausgeben lassen und dann alle Werte, die serialisiert werden auch. Oftmals sind die Eigenschaften dann leer und es ist kein Fehler von der JSON-Bibliothek.
    Wenn es das Problem wäre, was @dive26 beschrieben hat, dann wäre deine Datei korrupt und (wahrscheinlich!) nicht parsbar. Dann würde eine Exception geworfen.

    Ich habe über den Aufbau deiner Anwendung natürlich keine Ahnung, aber ich nehme gerade einfach mal an, dass du ein Fenster hast, in dem deine Einstellungen verwaltet werden. In dem Fall kannst du deine Einstellungen beim Schließen des Fensters wegspeichern, oder (wenn du wirklich paranoid sein willst) natürlich auch bei jeder Änderung. Aber je mehr Einstellungen du änderst und speichern willst, desto länger dauert der Prozess natürlich. Das spiegelt sich in der Performance deiner Applikation wieder.

    Was evtl. noch zu beachten wäre, ist wenn du Threads (schau, kein Faden! :D ) verwendest. Das kann wirklich immer solche merkwürdigen Effekte haben, dass Objekte plötzlich leer oder null sind. Meist sind das sog. Race-Conditions, wo zwei Teile deiner Applikation versuchen, auf den selben Speicher zuzugreifen.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Neu

    siycah schrieb:

    Das freut mich zu hören. Ich hoffe die Umstellung von C# zu VB war jetzt kein Riesenaufwand. Mit VB als Sprache konnte ich mich der Syntaxhalber nicht anfreunden

    Ne so kleine Fragmente krieg ich grad noch so hin :) Es gibt ja auch Übersetzer im Netz... Ich habe mich gerade wegen der Syntax für VB entschieden... Da hab dann zumindest ein End If nd kein } und weiss genau, wozu das dann gehört...

    Meine Einstellungen sind im Programm leider nicht alle in einem Fenster gesammelt, sondern "all over the place", wie es so schön auf Englisch heisst :-). Also auf verschiedenen Registern.

    Jetzt wo du es sagst mit den Threads: Das kann natürlich gut sein, mit Threads hatte ich schon so einige Probleme bei dieser Anwendung. Das mit den Race-Conditions leuchtet mir am ehesten ein...

    Neu

    kafffee schrieb:

    Ich habe mich gerade wegen der Syntax für VB entschieden...


    VB hat mMn viele Eigenheiten, die die Syntax für mich unverständlicher machen. Es gibt IMO auch etwas wie "zu verbos".
    Dass man:

    VB.NET-Quellcode

    1. public function Foo(ByVal x As String) As Boolean
    2. end function ' für eine Methode mit Rückgabewert
    3. ' und
    4. public sub Bar(ByVal y As Integer)
    5. end sub ' für eine Methode ohne Rückgabewert


    hat leuchtet mir nicht ein. Es hat natürlich auch einen gewissen Charm in vielen Sachen, z.B. das eingebaute Handling für Events.

    Aber ich finde die Klammern für mich übersichtlicher, vor allem wenn man Klammer-Highlighting hat.
    VB hat aber durchaus auch seine Daseinsberechtigung und ich finde es super, dass es darum eine aktive Community geht - auch wenn sie manchmal (wie es eigentlich auch überall ist) ziemlich toxisch werden kann.

    kafffee schrieb:

    Meine Einstellungen sind im Programm leider nicht alle in einem Fenster gesammelt


    Ah okay, ja gut. Dann macht es natürlich Sinn, die in einem Model zu sammeln. Dein Vorgehen, die Einstellungen beim Beenden des Programms zu speichern leuchtet mir dann noch mehr ein.
    Müssen wir nur noch dem Sache auf den Grund gehen, dass die Einstellungen nicht richtig gespeichert werden...

    kafffee schrieb:

    Das mit den Race-Conditions leuchtet mir am ehesten ein...


    Ich würde dir empfehlen, einen Profiler (z.B. den mitgelieferten) zu nutzen, um den Speicher mal genauer zu untersuchen.
    Früher gab's mal ein Tool, das hieß JINX. Damit konnte man u.a. Deadlocks und Race Conditions im Code finden. Zwar nicht statisch, aber zur Laufzeit. Ich weiß nicht ob es sowas auch für modernes .net gibt, wenn ich ehrlich bin!
    Für mich war es bisher relativ einfach auf dem Mac oder Linux .net direkt zu AARCH64 oder x86_64 zu compilen und dann Valgrind laufen zu lassen - aber ich entwickle heutzutage eher selten für Windows/mit einer UI. Und wenn, dann bevorzuge ich Technologien wie MAUI.

    Ansonsten kann ich dir ein paar Ratschläge geben:
    Achte drauf, wo deine Mutexe gerade sind und wer die gerade locked. (In .net ist das ja mit dem lock-Schlüsselwort).

    In C++ finde ich sowas ganz nützlich:

    C-Quellcode

    1. #include <mutex>
    2. #include <string>
    3. using std::lock_guard;
    4. using std::mutex;
    5. using std::string;
    6. class Foo {
    7. public:
    8. void foo1() {
    9. // hier wird nicht zwingend gelocked. Ich definiere, dass diese Methode einfach weniger Prio hat
    10. lock_guard myLock(m_mutex, std::defer_lock);
    11. if (condition) {
    12. if (!myLock.try_lock()) {
    13. // try again later
    14. logger.log("Mutex locked by {0:s}!", m_mutexLockedBy);
    15. return;
    16. }
    17. // Mutex wurde gelocked
    18. m_mutexLockedBy = __FUNCTION__;
    19. // nach allen Berechnungen würde der Mutex wieder freigegeben
    20. }
    21. }
    22. void foo2() {
    23. lock_guard myLock(m_mutex); // hier wird der Mutex gelocked!
    24. m_mutexLockedBy = __FUNCTION__;
    25. // hier am Ende nach allen Berechnungen/Mutationen wird der Lock freigegeben
    26. }
    27. private:
    28. mutex m_dataMutex;
    29. string m_mutexLockedBy;
    30. };


    Alternativ gibt es in C++ auch timed_mutexe, wo man noch ein Timeout mitgeben kann. Ganz praktisch wenn man mit Hochleistungsrechnern arbeitet.

    In C# würde ich das so oder so ähnlich schreiben:

    C#-Quellcode

    1. using System;
    2. using System.Threading;
    3. class MyClass {
    4. internal static object _myLock;
    5. internal static string _lockedBy;
    6. static MyClass() {
    7. _myLock = new object();
    8. }
    9. public void NotImportant() {
    10. if (Monitor.IsEntered(_myLock) {
    11. Console.Error.WriteLine("Object is locked by {0}", _lockedBy);
    12. return;
    13. }
    14. if (condition) {
    15. lock (_myLock) {
    16. _lockedBy = nameof(NotImportant);
    17. // do stuff
    18. }
    19. }
    20. }
    21. // In der wichtigen Methode dann einfach stumpf locken
    22. }


    Ob die C# Syntax jetzt genau richtig ist, weiß ich nicht. Habe ich gerade hier im Forum so geschrieben. Aber ich denke (hoffe) dass du das Beispiel verstehst.
    Du wirst damit vielleicht nicht im ersten Anlauf eine 100%ige Abdeckung bekommen, aber mit solchen Locks (auch statisch, also instanzübergreifend) wirst du einige solcher Race-Conditions vermeiden können. Es geht aber wie gesagt nichts über einen Memory Profiler und ein Tool. was Deadlocks finden kann!
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)