Laden...

Daten aus DataGrid per RestAPI speichern

Erstellt von Sascha87 vor 2 Jahren Letzter Beitrag vor 2 Jahren 805 Views
S
Sascha87 Themenstarter:in
48 Beiträge seit 2021
vor 2 Jahren
Daten aus DataGrid per RestAPI speichern

Verwendetes Datenbanksystem: MariaDB
Verwendete API: RestSharp

Hallo zusammen,
erneut habe ich eine Frage.
Aktuell lade ich einige Daten per RestApi in ein DataGrid. Die Anzeige, der darin enthaltenen Daten funktioniert soweit ganz gut.
Jetzt möchte ich die einzelnen Zeilen abändern und automatisch in der Datenbank speichern können.
Das speichern sollte ebenfalls mittels RestSharp funktionieren.

Nur, wie gehe ich da am besten vor? Wie bekomme ich die Inhalt der einzelnen Zeilen eines DataGrid.
Wie speichere ich den geänderten Wert automatisch? Oder geht es nur per Button?

Hier mein Code:


<DataGrid ItemsSource="{Binding LDKompetenzen}" AutoGenerateColumns="False" CanUserDeleteRows="False" CanUserAddRows="False" CanUserSortColumns="True" FontSize="12" Height="Auto" BorderThickness="0">
                <DataGrid.Resources>
                    <Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" TargetType="{x:Type DataGridColumnHeader}" >
                        <Setter Property="Background" Value="LightGray" />
                        <Setter Property="Foreground" Value="Black" />
                        <Setter Property="BorderBrush" Value="Black"/>
                        <Setter Property="BorderThickness" Value="1 1 1 1"/>
                        <Setter Property="Margin" Value="-1,-1,0,0" />
                        <Setter Property="Height" Value="28" />
                        <Setter Property="Width" Value="auto"/>
                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
                        <Setter Property="FontWeight" Value="SemiBold"/>
                        <Setter Property="FontStyle" Value="Italic"/>
                        <Setter Property="FontSize" Value="15"/>
                        <Setter Property="Padding" Value="15,0,15,0"/>
                    </Style>
                </DataGrid.Resources>
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Kompetenz" Binding="{Binding Kompetenz}" IsReadOnly="True">
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="TextBlock.Background" Value="{Binding Background}" />
                                <Setter Property="HorizontalAlignment" Value="Stretch"/>
                                <Setter Property="VerticalAlignment" Value="Stretch"/>
                                <Setter Property="TextAlignment" Value="Center"/>
                                <Setter Property="Padding" Value="0,20,0,0"/>
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTemplateColumn Header="Farben" IsReadOnly="True">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                    <Button Content="" Background="White" Margin="2" Padding="5,3,5,3"></Button>
                                    <Button Content="" Background="Yellow" Margin="2" Padding="5,3,5,3"></Button>
                                    <Button Content="" Background="LightGreen" Margin="2" Padding="5,3,5,3"></Button>
                                </StackPanel>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTextColumn Header="Stufe" Binding="{Binding Stufe, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" >
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                                <Setter Property="VerticalAlignment" Value="Center"/>
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTextColumn Header="Niveau" Binding="{Binding Niveau, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                                <Setter Property="VerticalAlignment" Value="Center"/>
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTextColumn Header="Ergebnis" Binding="{Binding Ergebnis, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                                <Setter Property="VerticalAlignment" Value="Center"/>
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTextColumn Header="Note" Binding="{Binding Note, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                                <Setter Property="VerticalAlignment" Value="Center"/>
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                    <DataGridTemplateColumn Header="Datum">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <DatePicker SelectedDate="{Binding Datum, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" BorderThickness="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTextColumn Header="Kürzel" Binding="{Binding Kuerzel, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">
                        <DataGridTextColumn.ElementStyle>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                                <Setter Property="VerticalAlignment" Value="Center"/>
                            </Style>
                        </DataGridTextColumn.ElementStyle>
                    </DataGridTextColumn>
                </DataGrid.Columns>
            </DataGrid>

Ich bin für einige Gedankenansetze von euch sehr Dankbar!

Grüße
Sascha

T
2.219 Beiträge seit 2008
vor 2 Jahren

Laut Doku, hat das DataGrid ein RowEditEnding Event, was du vermutlich abonieren müsstest.
In der Doku dürftest du auch einen Ansatz dafür finden.

Doku:
DataGrid.RowEditEnding Ereignis (System.Windows.Controls)

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 hast doch per Binding die Eigenschaft LDKompetenzen als ItemsSource angegeben.
Solange du nach MVVM vorgegangen bist und dessen Klasse INotifyPropertyChanged implementiert hast, werden automatisch alle Änderungen der DataGrid-Werte zurück geschrieben.
Du brauchst also nur dieses LDKompetenzen-Objekt mittels RestSharp serialisieren.
Welchen genauen Datentyp hat denn diese Eigenschaft?

Und was genau meinst du mit "automatisch"? Sollen nach jeder Änderung sofort alle Daten zurückgeschrieben werden?
Oder möchtest du, daß nur die geänderten Daten ermittelt werden? Dann schau mal in How do you handle data grid cell changes with MVVM? sowie Apply changes to Model when a row of DataGrid is changed using MVVM.

@T-Virus: Dein Ansatz ist aber nicht MVVM-konform.

16.806 Beiträge seit 2008
vor 2 Jahren

Wie speichere ich den geänderten Wert automatisch? Oder geht es nur per Button?

Es empfiehlt sich a) sowas nicht automatisch zu speichern und b) auch einen Lock-Mechanismus einzubauen.
Die Erfahrung zeigt, dass sich ansonsten die Anwender gegenseitig Zeug überschreiben ohne, dass es bemerkt wird.

Optimistische Nebenläufigkeit - ADO.NET ist ein relativ alter Artikel, der noch auf dem DataSet beruht; erklärt aber die Prinzipien.
Im Falle von REST musst das eh selbst implementieren (was auch gut so ist).

T
2.219 Beiträge seit 2008
vor 2 Jahren

@Th69
Hatte ich schon vermutet.
Bin leider nicht sehr aktiv mit WPF unterwegs, weshalb ich nur oberflächlich mit den Konzepten vertraut bin.

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

Vielen Dank für die Links.
Beim ersten Link: Ich nutze .NET 4,75 (Galasoft geht nur bis 4.5). Daher kann ich keine "Galasoft WPF" hinzufügen.

Link 2: Ich nutze in meiner ViewModel INotifyPropertyChanged.

Funktioniert es, wenn ich eine entsprechende Funktion in die Viewmodel schreibe mit IEditableObject?

4.931 Beiträge seit 2008
vor 2 Jahren

Das MVVM Light Toolkit sollte eigentlich auch mit .NET 4.7 funktionieren, nur mit dem aktuellen .NET 5 wohl bisher nicht: .NET Framework 4.7 to .NET 5 Migration with Galasoft MVVMLight Library.

Aber ich denke auch, daß IEditableObject der einfachere Weg ist.
Du hast doch sicherlich LDKompetenzen als ObservableCollection<X> deklariert? Dann mußt du IEditableObject für die Klasse X implementieren.

T
2.219 Beiträge seit 2008
vor 2 Jahren

@Sascha87
Was ist .NET 4,75?
Die Version ist mir neu. 🙂

Link:
Bestimmen der installierten .NET Framework-Versionen

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 weiß nicht was mich da geritten hat.....4.7.2 meine ich.

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

Genau, die Klasse ist als ObservableCollection deklariert. In diese Klasse kann ich dann eine weitere Methode mit IEditableObject einfügen?
Es tut mir Leid, es ist Neuland für mich 🙄

4.931 Beiträge seit 2008
vor 2 Jahren

Hast du noch nie eine Schnittstelle implementiert (ist fast identisch zum Ableiten einer Basisklasse)?

So wie es auch in dem Link steht (nur statt RowViewModel in deine Klasse diese Schnittstelle hinzufügen sowie die 3 Methoden einfügen - wobei du dann nur EndEdit mit Code füllst).

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

Also, folgendes habe ich gemacht:

ViewModel:
Die Klasse erweitert:


class LDViewModel : NotifyPropertyBase, IEditableObject

Anschließend folgendes gefüllt:


    public void EndEdit()
        {
            var client = new RestClient("https://xxxxxxxxde/");
            var request = new RestRequest("ld/LDUpdate/{name}/{kompetenzID/{ergebnis}/{stufe}/{niveau}/{note}/{datum}/{kuerzel}", Method.POST)
                .AddUrlSegment("name", Name)
                .AddUrlSegment("kompetenzID", LDCurrentKompetenz.KompetenzID)
                .AddUrlSegment("ergebnis", LDCurrentKompetenz.Ergebnis)
                .AddUrlSegment("stufe", LDCurrentKompetenz.Stufe)
                .AddUrlSegment("niveau", LDCurrentKompetenz.Niveau)
                .AddUrlSegment("note", LDCurrentKompetenz.Note)
                .AddUrlSegment("datum", LDCurrentKompetenz.Datum)
                .AddUrlSegment("kuerzel", LDCurrentKompetenz.Kuerzel)
                .AddHeader("Authorization", "Bearer " + ViewFingerPrint);

            IRestResponse restResponse = client.Execute(request);

            if (restResponse.StatusCode == HttpStatusCode.BadRequest)
            {
                MessageBox.Show("Irgendetwas stimmt mit der Verbindung nicht.");
            }
            else
            {
                MessageBox.Show("Die Leistungsdokumentation wurde erfolgreich aktualisiert.");
            }
        }

Allerdings reagiert er nicht. Woher weiß er, welches DataGrid gemeint ist?

4.931 Beiträge seit 2008
vor 2 Jahren

Das erscheint mir die falsche Klasse zu sein?!
Du mußt die Schnittstelle in der Klasse implementieren, welche eine Zeile deiner Daten entspricht (schau oben noch mal in meinen Beitrag bzgl. der Klasse X).

Wenn du doch die eigentliche Funktionalität (RestAPI) in deinem Hauptviewmodel implementieren möchtest, dann erzeuge in deiner Klasse X ein (statisches) Ereignis (s.a. [FAQ] Eigenen Event definieren / Information zu Events (Ereignis/Ereignisse)) und abonniere es von deinem Hauptviewmodel.

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

Das ist meine ViewModel.
In deinem Beispiel sowie unzählige andere wurde es dort implementiert.

4.931 Beiträge seit 2008
vor 2 Jahren

Nein, in meinem Link steht explizit RowViewModel (Row = Zeile), nicht das ViewModel, in dem die ItemsSource definiert ist.

Zeige doch mal, wie du die Eigenschaft LDKompetenzen in deinem Hauptviewmodel definiert hast.

Edit: Schau auch mal in Implementing IEditableObject in model in mvvm pattern, am i breaking pattern? (als Antwort hat der Beitragsersteller 'first100' seinen Code mit der Klasse Company, welche IEditableObject implementiert, und dem ViewModel mit der Eigenschaft SearchCompanies gepostet).

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

Ok, dann habe ich das falsch verstanden.

Die LDKompetenzen hole ich so in das ViewModel:


public LDKompetenzen LDKompetenzen { get; set; }

So lade ich die "Current" Version:


LDKompetenz _ldcurrentKompetenz = null;
        public LDKompetenz LDCurrentKompetenz
        {
            get { return _ldcurrentKompetenz; }
            set
            {
                if (value == null)
                    return;
                OnPropertyChanged(ref _ldcurrentKompetenz, value);
            }
        }

So lade ich die Daten in die View:


LDKompetenzen = LDKompetenzen.Load(LDCurrentFach.FachID, value.ThemaID, ViewFingerPrint, Name);

4.931 Beiträge seit 2008
vor 2 Jahren

Und was ist jetzt die Klasse LDKompetenzen (du hast doch auch von ObservableCollection<...> geschrieben)?

So wie ich deine Eigenschaft "LDCurrentKompetenz" richtig verstehe, solltest du also dann in der Klasse LDKompetenz das Interface IEditableObject implementieren.

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

Das ist hier meine Klasse:



class LDKompetenzen : ObservableCollection<LDKompetenz>
    {
        private static readonly LDKompetenzen _kompetenzen = new LDKompetenzen();

        private LDKompetenzen() { }

        public static LDKompetenzen Load(string fachID, string themenID, string ViewFingerPrint, string name)
        {
            _kompetenzen.Clear();
            try
            {
                var client = new RestClient("MEINEDOMAIN");

                var request = new RestRequest("ld/kompetenzen/{fachid}/{themenid}/{SHDID}", Method.GET)
                .AddHeader("Authorization", "Bearer " + ViewFingerPrint)
                .AddUrlSegment("fachid", fachID)
                .AddUrlSegment("SHDID", name)
                .AddUrlSegment("themenid", themenID);

                IRestResponse restResponse = client.Execute(request);
                HttpStatusCode statusCode = restResponse.StatusCode;
                if ((int)statusCode != 401)
                {
                    var arr = JsonConvert.DeserializeObject<JArray>(restResponse.Content);
                    foreach (JObject obj in arr)
                    {
                        var KompetenzID = (string)obj["id"];
                        var Kompetenz = (string)obj["Kompetenz"];
                        var SHDID = (string)obj["SHDID"];
                        var Ergebnis = (string)obj["Ergebnis"];
                        var Stufe = (string)obj["Stufe"];
                        var Niveau = (string)obj["Niveau"];
                        var Note = (string)obj["Note"];
                        var Datum = (string)obj["Datum"];
                        var Kuerzel = (string)obj["Kuerzel"];
                        var background = (string)obj["background"];

                            _kompetenzen.Add(new LDKompetenz(KompetenzID, Kompetenz, SHDID, Ergebnis, Stufe, Niveau, Note, Datum, Kuerzel, background));
                    }
                }
                else
                {
                    var KompetenzID = "0";
                    var Kompetenz = "";
                    var SHDID = "";
                    var Ergebnis = "";
                    var Stufe = "";
                    var Niveau = "";
                    var Note = "";
                    var Datum = "";
                    var Kuerzel = "";
                    var background = "white";

                    _kompetenzen.Add(new LDKompetenz(KompetenzID, Kompetenz, SHDID, Ergebnis, Stufe, Niveau, Note, Datum, Kuerzel, background));
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
            return _kompetenzen;
        }
    }

Und die greift auf folgende zu:



        public LDKompetenz(string kompetenzID, string kompetenz, string shdID, string ergebnis, string stufe, string niveau, string note, string datum, string kuerzel, string background)
        {
            KompetenzID = kompetenzID;
            Kompetenz = kompetenz;
            SHDID = shdID;
            Ergebnis = ergebnis;
            Stufe = stufe;
            Niveau = niveau;
            Note = note;
            Datum = datum;
            Kuerzel = kuerzel;
            Background = background;
        }

        //Anzeige Fördermaßnahmen
        public string KompetenzID { get; set; }
        public string Kompetenz { get; set; }
        public string SHDID { get; set; }
        public string Ergebnis { get; set; }
        public string Stufe { get; set; }
        public string Niveau { get; set; }
        public string Note { get; set; }
        public string Datum { get; set; }
        public string Kuerzel { get; set; }
        public string Name { get; set; }
        public string Background { get; set; }

        public bool IsSelected { get; set; }

        public override string ToString()
        {
            return $"{Kompetenz}";
        }
    }


4.931 Beiträge seit 2008
vor 2 Jahren

OK, und wie ich vermutet habe, mußt du es in der Klasse LDKompetenz implementieren. Dort hast du dann Zugriff auf alle Member der aktuell geänderten Datenzeile.

PS: Deinen Namen und E-Mail solltest du besser wieder wegeditieren.

T
2.219 Beiträge seit 2008
vor 2 Jahren

Nachträglich noch der Tipp:
Wenn du Strings hardcoden musst, dann macht das über Konstanten.
Literale im Code sind eine sehr unschöne Sache und gehen auch bei einigen Umsetzungen auch in Richtung Magic Strings.

Auch wäre es besser wenn du anstelle von "" als Wert String.Empty verwendest.
Durch "" im Code erzeugst du zusätzliche Literale, was die Laufzeitumgebung zwar abfängt aber durch String.Empty wird eine statische Instanz verwendet und der Code ist auch klarer ausgedrückt.

Auch würde ich empfehlen die Deserialierung des Json umzubauen.
Was du im Grund hast, ist eine Liste von Objekten.
Du kannst hier einfach List<T> als Container verwenden und eine entsprechende Klasse anlegen, die ein einzelnes Objekt der Api abbildet.
Dadurch sparst du dir den Aufwand die Eigenschaften einzeln auslesen und die Liste selbst aufbauen zu müssen.
Kann an der Stelle der Deseralisierer übernehmen.
Spart dir unnötigen Aufwand und Zeit bei der Umsetzung 🙂

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

Ok, das teste ich mal.

Ich benötige noch die Variabel "Name" aus der ViewModel. Das ist eine TextBox aus der View.

Dazu habe ich folgendes in der ViewModel:


        private string _name = "1";
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                if (value == "" && value == null)
                    return;
                _name = value;
                LDKompetenzen = LDKompetenzen.Load(LDCurrentFach.FachID, LDCurrentThema.ThemaID, ViewFingerPrint, Name);
                OnPropertyChanged(ref _name, value);
            }
        }

Kann ich es ganz normal mit Name {get set} in der LDKompetenz laden?

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

Nachträglich noch der Tipp:
Wenn du Strings hardcoden musst, dann macht das über Konstanten.
Literale im Code sind eine sehr unschöne Sache und gehen auch bei einigen Umsetzungen auch in Richtung Magic Strings.

Auch wäre es besser wenn du anstelle von "" als Wert String.Empty verwendest.
Durch "" im Code erzeugst du zusätzliche Literale, was die Laufzeitumgebung zwar abfängt aber durch String.Empty wird eine statische Instanz verwendet und der Code ist auch klarer ausgedrückt.

Auch würde ich empfehlen die Deserialierung des Json umzubauen.
Was du im Grund hast, ist eine Liste von Objekten.
Du kannst hier einfach List<T> als Container verwenden und eine entsprechende Klasse anlegen, die ein einzelnes Objekt der Api abbildet.
Dadurch sparst du dir den Aufwand die Eigenschaften einzeln auslesen und die Liste selbst aufbauen zu müssen.
Kann an der Stelle der Deseralisierer übernehmen.
Spart dir unnötigen Aufwand und Zeit bei der Umsetzung 🙂

T-Virus

Ok, danke für die Tipps! Dann werde ich mir mal anschauen wie es mit den Listen funktioniert.

4.931 Beiträge seit 2008
vor 2 Jahren

Wofür ist denn in der Klasse LDKompetenz die bisher nicht benutzte Eigenschaft Name gedacht?

Der design-technisch beste Weg wäre wirklich das Erzeugen eines Ereignisses (mit dem aktuellen LDKomptenz-Objekt als Eigenschaft), so daß die ViewModel-Klasse (bzw. weiterdelegiert an eine Datenzugriffsklasse) dann die eigentliche RestAPI-Serialisierung durchführt.

Falls du hier nur genau ein LDKompetenzen-Objekt in deinem Projekt hast, könntest du auch dort eine public static string Name-Eigenschaft anlegen, welche dann von der Load-Methode gesetzt wird: Name = name.

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

Vielen vielen Dank! Mein Verständnis erweitert sich und es funktioniert fast.

Ein einziges Problem habe ich noch. Das Datum gebe ich als String weiter.

Den string ändere ich ab, damit es kein Backslash im string ist


var newdate = Datum.Replace(@"/", "-");

Allerdings lässt er die Uhrzeit. Wie bekomme ich die wohl weg?

4.931 Beiträge seit 2008
vor 2 Jahren

Ein Datum solltest du immer als DateTime(Offset) speichern und nur für die Ausgabe in ein bestimmtes Format als String umwandeln (Parse/ParseExact sowie ToString(format)), s.a. das aktuelle Thema c# Datum umwandeln?

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

Das hatte ich ursprünglich. Leider bekomme ich beim JSon Objekten Probleme:


var arr = JsonConvert.DeserializeObject<JArray>(restResponse.Content);
                    foreach (JObject obj in arr)
                    {
                        var KompetenzID = (string)obj["id"];
                        var Kompetenz = (string)obj["Kompetenz"];
                        var SHDID = (string)obj["SHDID"];
                        var Ergebnis = (string)obj["Ergebnis"];
                        var Stufe = (string)obj["Stufe"];
                        var Niveau = (string)obj["Niveau"];
                        var Note = (string)obj["Note"];
                        DateTime Datum = (DateTime)obj["Datum"];
                        var Kuerzel = (string)obj["Kuerzel"];
                        var background = (string)obj["background"];

                        _kompetenzen.Add(new LDKompetenz(KompetenzID, Kompetenz, SHDID, Ergebnis, Stufe, Niveau, Note, Datum, Kuerzel, background, name, ViewFingerPrint));
                    }

Ich habe Tabelle 1 mit 20 Daten. In Tabelle 2 habe ich die Nutzerinformationen dazu. Nicht zu jeder Zeile aus Tabelle 1 gibt es Nutzerdaten. Daher kann es passieren, dass DateTime Datum = null ist.
Dann bekomme ich eine Fehlermeldung (siehe Bild).

16.806 Beiträge seit 2008
vor 2 Jahren

Bitte keine Exceptions als Bilder anfügen. Das erschwert vieles, verwende das ERROR-Tag. Danke Dir.
Siehe auch [Hinweis] Wie poste ich richtig?

Ansonsten nimm den Hinweis von Th69 an: REST gibt eigentlich vor, dass die ISO 8601 Regel beachtet wird (machen einige leider nicht).
Siehe auch [FAQ] DateTime vs. DateTimeOffset und der Umgang mit Zeiten in .NET
Dass Du versuchst null in ein Objekt zu casten ist halt nen klassischer Fehler im Code. Musst halt vorher auf null prüfen.

Du könntest Dir as ganze manuelle Casten sparen, wenn Du zB statt dem eher angestaubten RestClient einfach Refit verwenden würdest.
Ansonsten kannst Du zumindest die meisten Fälle mit dem eingebauten Serializer abdecken:


var response = await client.PostAsync<TYPEHERE>(request);

Den Uhrzeitpattern kannst Du definieren: Serialization | RestSharp

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

Vielen lieben Dank Th69!
Soweit klappt alles.
Das Einzige, das mir aufgefallen ist, der Speichert die Daten erst in der Datenbank, wenn man alle Zellen einmal angeklickt hat.
Manche Zellen sollen zwischendurch leer bleiben. Wäre das in der Umsetzung noch möglich? Oder muss ich eher auf CellEditEnding gehen?

4.931 Beiträge seit 2008
vor 2 Jahren

Die Methode EndEdit sollte aufgerufen werden, sobald Änderungen an einer Zelle gemacht wurden und man dann eine andere Zeile selektiert (bzw. fokussiert).
Dann solltest du mal deinen Code genauer debuggen, ob und wann diese Methode aufgerufen wird?

Wenn du wirklich nach jeder Zellenwertänderung die Daten speichern möchtest, dann bräuchtest du wirklich so etwas wie "CellEditEnded" (CellEditEnding wird jedoch aufgerufen, bevor die Daten gespeichert wurden, s.a. WPF DataGrid - Committing changes cell-by-cell).
Dies halte ich aber persönlich generell für nicht so toll.

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

Du hast recht, die Methode wird immer aufgerufen.

Mein RestAPI Link wird nur nicht korrekt gefüllt. Daher gibt es auch keine Einträge in der Datenbank.

4.931 Beiträge seit 2008
vor 2 Jahren

Kommt die AddUrlSegment-Methode mit null-Werten klar? In der Doku RestSharp: Interface IRestRequest finde ich nichts detailliertes dazu.

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

Leider nein. Mir würde ein Default Value wie "-" reichen.

16.806 Beiträge seit 2008
vor 2 Jahren

Dann darfst eben nich blind die Parameter reinhängen, sondern musst vorher prüfen.
Wenn es null ist, lass es weg.

4.931 Beiträge seit 2008
vor 2 Jahren

Das programmiertechnisch einfachste wäre dann wohl eine Erweiterungsmethode (in einer eigenen statischen Klasse) zu erstellen:


IRestRequest CheckAddUrlSegment(this IRestRequest request, string name, string value)
{
    if (value != null)
        return request.AddUlrSegment(name, value);

    return request;
}

So brauchst du nur beim Aufruf jeweils den Methodennamen zu ändern.

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

Gibt es die Möglichkeit, wenn ich keinen Wert habe, dass dann "-" gesetzt wird?

Auch, wenn es nicht der Ansicht von Abt entspricht, finde ich den Weg ganz gut. So hätte ich keine leeren Felder in der DB und beim auslesen habe ich direkt ein Zeichen in der Liste, das zeigt, dass dort keine Werte vorhanden sind und ergänzt werden müssen.
Denn genau das Zeichen setze ich so oder so als


TargetNullValue=-

16.806 Beiträge seit 2008
vor 2 Jahren

Das hat mit "der Ansicht von Abt" nix zutun; das würde einfach viele Standards "verletzen" und Dir sehr wahrscheinlich in Zukunft sehr viele Probleme machen.

Zeichen in der Liste, das zeigt, dass dort keine Werte vorhanden sind und ergänzt werden müssen.

Sowas würde man in der UI mappen, aber niemals über "Datenlogik".

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

Gut, dann anders.

Wenn ich die Methode von Th69 richtig verstehe, wird nur der Wert an die URL gehängt, der auch gefüllt ist, richtig?

Ich bekomme auf der anderen Seite dann Schwierigkeiten. Das Script auf dem Server erwartet genau die URL.
Woher soll das Script denn wissen, wenn die Werte in einer ganz anderen Reihenfolge versendet werden?

16.806 Beiträge seit 2008
vor 2 Jahren

Woher soll das Script denn wissen, wenn die Werte in einer ganz anderen Reihenfolge versendet werden?

Null sind prinzipiell valide Werte und in HTTP erlaubt. Die meisten Bibliotheken lassen den Wert einfach beim Versenden Weg oder er ist leer.
Serverseitig wird dann null gemappt, wenn nach dem jeweiligen Key gefragt wird aber kein Wert übertragen wurde.

Die Reihenfolge darf keine Rolle spielen, weil diese bei HTTP-Anfragen generell nicht garantiert sind; weder beim Versenden noch beim Empfangen.
Spielt in Deinem Code die Reihenfolge eine Rolle, dann hast Du generell einen Fehler in der Verarbeitung. Da sind Folgefehler demnach nur eine Frage der Zeit; also machs direkt richtig: und halte Dich einfach an Standards. Dafür sind sie da.
HTTP Parameter haben immer ein Key-Value-Schema, sodass Du das problemlos anhand der Namen verarbeiten kannst und einen grundlegenden Fehler wie eine stupide Reihenfolge erst gar nicht machen musst.

Wenn Du es korrekt implementiert hast, wie es der Standard (in diesem Fall REST) vorsieht, dann kannst Du problemlos null-Werte empfangen.
In einem weiteren Schritt validierst Du Pflicht- und optionale Felder (nach ihren Werten).

Danach schreibst Du die Werte in die Datenbank; inkl. null, wo es erlaubt ist.