Änderungen im DataGridView an Datenbank übergeben

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Änderungen im DataGridView an Datenbank übergeben

    Hallo,

    ich steh ein bisschen auf dem Schlauch wie ich das umsetze:
    Also das geht schonmal, aber wahrscheinlich sollte das so garnicht funktionieren dürfen siehe unten:

    VB.NET-Quellcode

    1. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    2. Using DBZ As New DBZugriff("Select * from DasIstEinTest")
    3. Using da As SqlDataAdapter = DBZ.DBAdapter
    4. da.Fill(DS1.DasIstEinTest)
    5. DS1.DasIstEinTest(2).col4 = 5 'Zeile 6
    6. Using CmdBuilder As New SqlCommandBuilder(da)
    7. CmdBuilder.GetUpdateCommand()
    8. da.Update(DS1.DasIstEinTest)
    9. End Using
    10. End Using
    11. End Using
    12. End Sub


    Ich habs jetzt einfach versucht an Zeile 6 auseinanderzuschneiden, da wird allerdings nix mehr geupdatet, der UpdateCommand ist Nothing, (aber das ist er im obigen Beispiel auch...):

    Quellcode

    1. Private DBZ as New DBZugriff("Select * from DasIstEinTest")
    2. Private da As SqlDataAdapter
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    4. da = DBZ.DBAdapter 'eigene Funktion
    5. da.Fill(DS1.DasIstEinTest)
    6. End Sub
    7. 'Zeile 6 macht jetzt der User
    8. Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
    9. Using CmdBuilder As New SqlCommandBuilder(da)
    10. CmdBuilder.GetUpdateCommand()
    11. da.Update(DS1.DasIstEinTest)
    12. End Using
    13. End Sub


    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    normalerweise speichert man geänterte Daten einfach mit

    VB.NET-Quellcode

    1. TableAdapterManager.UpdateAll(mytypedDataset)
    zurück.
    Funktioniert zwar nie, aber so ist es gedacht: Eine Zeile - mehr nicht.

    Alternativ kann man Dataset->Db verwenden, das funktioniert - man musses aber einbinden.

    Von deim Code verstehe ich nix, schon weil ich nicht weiss, was ein New DBZugriff ist, und was du damit vorhast.
    Sieht mir an verschiedenen Stellen doppelt und dreifach gemoppelt aus - solch bringt meist keine guten Ergebnisse.
    Die DBZugriff hab ich nur damit ich weiß wo welche Verbindung dransitzt, das ist in dem Beispiel hier theoretisch überflüssig. Da wird eine New SqlConnection mit einem festen Verbindunsgstring aus einer txt erzeugt und dann der DataAdapter mit dieser Connection und dem SelectCommand.

    Das hier ist quasi dasselbe in grün:

    Quellcode

    1. Private Conn as New SqlConnection("verbindungsstring")
    2. Private da As New SqlDataAdapter("Select * from DasIstEinTest", Conn)
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    4. da.Fill(DS1.DasIstEinTest)
    5. End Sub
    6. 'Zeile 6 macht jetzt der User
    7. Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
    8. Using CmdBuilder As New SqlCommandBuilder(da)
    9. CmdBuilder.GetUpdateCommand()
    10. da.Update(DS1.DasIstEinTest)
    11. End Using
    12. End Sub


    In deinem Link schreibst du den TableAdapterManager gibts nur bei den Connectoren, leider habe ich diese Assistenzfunktion nie zum Laufen gekriegt mit meiner DB. Aber wenn es eh nie funktioniert...
    Ich schaue mir deinen Vorschlag mal durch. Ich möchte eigentlich immer nur so wenig wie nötig verwenden. Die vorgebastelten Sachen können ja immer gleich alles.
    und - funktionierts?
    wenn ja, provierma, ob du #11 löschen kannst - sieht mir unsinnig aus.

    Tatsächlich sollte es sogar noch deutlich simpler gehen:

    VB.NET-Quellcode

    1. Private Conn As New SqlConnection("verbindungsstring")
    2. Private da As New SqlDataAdapter("Select * from DasIstEinTest", Conn)
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    4. Dim CmdBuilder As New SqlCommandBuilder(da)
    5. da.Fill(DS1.DasIstEinTest)
    6. End Sub
    7. 'Zeile 6 macht jetzt der User
    8. Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
    9. da.Update(DS1.DasIstEinTest)
    10. End Sub
    Der Adapter braucht nur einmalig mit einem CommandBuilder initialisiert werden.
    Probiern kann ich noch nicht, aber das würd mich schon irre machen, wenn das ohne extra Verpackung plötzlich funktioniert.

    Nachdem ich deinen Vorschlag gelesen habe denke ich, meinst du #10 macht von vorneherein schon die ganze Arbeit und dann ist #11 überflüssig.
    Das mit dem CommandBuilder begreif ich allerdings auch überhaupt nicht, denn der UpdateCommand des DA ist immer leer, selbst wenn die DB ein Update erhält...
    Ich schätze das meintest du mit
    (das ist ein eigenartiges Design (nicht von mir): der CB wird nirgends sonst angesprochen - dennoch tut er seinen Dienst im DataAdapter)


    Ich hätte da aber noch eine Frage zu SqlInjection, da hab ich auch über ein paar Links zu dem hier gefunden.
    Warum genau funktioniert so eine Injection hier:


    'Der normale NoGo-Progger täte gar Textboxen verwenden (statt hier: DateTimePicker), sodass Angreifer dort ihre Sql-Injection-Angriffe reinschreiben können.
    Dim sql = "Select * From [Bestellung] where [Datum] between #" & dtpVon.Value & "# and #" & dtBis.Value & "#"
    Dim cmd = New SqlCeCommand(sql, _Con)


    aber hier nicht:


    Dim cmd = _Con.CreateCommandX( _
    "Select * From [Bestellung] where [Datum] between ? and ?", dtpVon.Value, dtBis.Value)


    In dtBis.Value kann doch auch irgendwas komisches stehen?

    Ich habe an solchen Stellen eigentlich nur Zahlen drin und vorher läuft Integer.TryParse, dann kann ich dem User schon frühzeitig eine Fehlermeldung geben. Kann man das auch Hacken?

    Haudruferzappeltnoch schrieb:

    Nachdem ich deinen Vorschlag gelesen habe denke ich, meinst du #10 macht von vorneherein schon die ganze Arbeit und dann ist #11 überflüssig.
    Naja, im weitesten Sinne.
    Tatsächlich ist #11 üflüssig, weil es überhaupt gar nichts macht.
    .GetUpdateCommand() gibt - wie der Name sagt - ein UpdateCommand zurück.
    Das verwendest du aber garnet - weist es keiner Variablen zu und nichts - einfach sinnlos, also weg damit!



    Das annere, worauf du nicht eingehst, ist aber wichtiger:

    ErfinderDesRades schrieb:

    Der Adapter braucht nur einmalig mit einem CommandBuilder initialisiert werden.
    Damit meine ich, ein DataAdapter braucht nur einmal mit einem CommandBuilder initialisiert zu werden.
    Wenn das bereits im Form_Load passiert, wie in post#2 gezeigt, dann braucht es im DGV_CellEndEdit() nicht immer wieder erneut geschehen.



    Haudruferzappeltnoch schrieb:

    Das mit dem CommandBuilder begreif ich allerdings auch überhaupt nicht, denn der UpdateCommand des DA ist immer leer
    Ja, das ist komisch:
    Ein DataAdapter kann updaten, wenn er mit einem CommandBuilder initialisiert wurde.
    Das funktioniert, obwohl das verwendete UpdateCommand nachwievor im DataAdapter nicht sichtbar wird.
    Wie gesagt: dieses eigenartige Design ist nicht von mir.



    Zur Sqlinjection-Frage:
    Sql-Code wird in der Sql-Engine irgendwie in nativen Code übersetzt und ausgeführt.
    Nur der Sql-Code!!
    Also der Sql-Code wird übersetzt.
    Nochmal: Nur der Sql-Code!!

    Der Wert eines SqlParameters wird nicht übersetzt.
    Sondern im übersetzten nativen Code gibts eine Art native Variable, die wird befüllt - der native Code ändert sich dadurch nicht.
    Du kannst also jeden Mist in die Variable schreiben - der zur Ausführung kommende native Code ändert sich dadurch nicht.

    Nu guck nochmal das SqlInjection-Tut, wie sich da das Sql komplett ändern kann, wenn man dem User quasi erlaubt, String-Schnipsel in den Sql-Code einzufügen.

    Das ist wie beim Kokain-Schmuggel: Du kannst es vorm Grenzübergang einfach so schlucken - fiel Fergnügen!
    Üblicherweise tut mans aber in ein Kondom, und schluckt das.

    Ich habe an solchen Stellen eigentlich nur Zahlen drin und vorher läuft Integer.TryParse, dann kann ich dem User schon frühzeitig eine Fehlermeldung geben. Kann man das auch Hacken?
    Wahrscheinlich nicht.
    Aber wenn du irgendwann mal Textwerte verwenden willst (oder kannst du das für immer ausschliessen?), dann musst du es ja doch erlernen, mit DbParametern umzugehen. (Die Gefahr besteht, dass du dir was gefährliches unverantwortliches(!) angewöhnst.)
    Warum also mit zwei verschiedenen Vorgehensweisen rummurkeln, anstatt gleich von vornherein, einheitlich, sicher vorzugehen?
    Letzteres ist einfacher und sicherer.

    Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Naja einfacher fand ichs jetzt nicht, ehrlich gesagt weiß ich nicht mal was davon denn so ein DbParameter genau war. hier hab ich geguckt, vielleicht nicht richtig?

    So wie ich es verstanden habe würde wenn dtpVon.Value einen Injection String beinhaltet, das ganze Problem weiterhin bestehen. An welcher Stelle wird das denn verhindert?
    jo, das Tutchen ist nicht falsch.
    Und diesen Teil hast du übersehen?

    Also eine string-frickelige Abfrage mag so aussehen:

    VB.NET-Quellcode

    1. 'Der normale NoGo-Progger täte gar Textboxen verwenden (statt hier: DateTimePicker), sodass Angreifer dort ihre Sql-Injection-Angriffe reinschreiben können.
    2. Dim sql = "Select * From [Bestellung] where [Datum] between #" & dtpVon.Value & "# and #" & dtBis.Value & "#"
    3. Dim cmd = New SqlCeCommand(sql, _Con)


    Demgegenüber sähe die Verwendung meiner Extension so aus:

    VB.NET-Quellcode

    1. Dim cmd = _Con.CreateCommandX( _
    2. "Select * From [Bestellung] where [Datum] between ? and ?", dtpVon.Value, dtBis.Value)
    Findest du nicht das zweite Snippet bischen einfacher?

    Aber es kommt ja (grad mit Datums) noch doller: Wenn du Zahlen oder Datumse in den String einfrickelst musst du berücksichtigen, welche Kultur auf dem SqlServer läuft, und entsprechend formatieren.
    Bei DbParametern ist ein Integer ein Integer und ein Date ein Date.
    Formatieren entfällt und noch ein Problem weniger.
    Ja das ist einfacher aber die Funktion CreateCommandX gehört ja erstmal auch dazu.

    Ich denke der relevante Part passiert hier: Dim param = cmd.Parameters.AddWithValue("@a" & i, values(i)) : splits(i) = splits(i) & param.ParameterName

    Wenn ich dtpVon.Value = "Kuckuck" habe dann habe ich oben den String: "Select * From [Bestellung] where [Datum] between #Kuckuck# and...
    und unten: "Select * From [Bestellung] where [Datum] between @a0 and... mit @a0 = "Kuckuck"

    Ich schätze @a0 ist der DbParameter, aber der Unterschied ist mir nicht klar.