Dataset only -> DB / Welches System und wie?

  • VB.NET
  • .NET 4.0

Es gibt 93 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    Läuft nun alles wie gewünscht, DataSet ist komplett mit Daten in die Datenbank integriert und laden, speichern, update etc. funzt wie es soll :thumbup:
    Vielen Dank nochmal an @ErfinderDesRades für deine Hilfe. Dann kann das Projekt nun mit ein paar Leuten durchgetestet werden :thumbsup:
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @ErfinderDesRades:

    Ist es möglich den folgenden Codeblock so umzubauen, dass er eine gewählte Tabelle, samt übergeordneten leert und neu befüllt aus der Datenbank heraus?
    Oder reicht es nur die betroffene Tabelle zu leeren und neu zu laden?

    VB.NET-Quellcode

    1. Public Sub CustomFill(table As DataTable, sqlAfterFrom As String, ParamArray args() As Object)
    2. ClearRecursive(table)
    3. Using adp = DirectCast(DirectCast(_Adapters(table), ICloneable).Clone, OleDbDataAdapter)
    4. Dim cmd = adp.SelectCommand
    5. cmd.CommandText &= " " & sqlAfterFrom
    6. For i = 0 To args.Length - 1
    7. cmd.Parameters.AddWithValue("@p" & i, args(i))
    8. Next
    9. _Con.Open()
    10. adp.Fill(table)
    11. _Con.Close()
    12. End Using
    13. End Sub


    Ich hab' damit vor, dass vor jeder Bearbeitung eines Datensatzes die Tabellen neu initialisiert werden - anstelle des ganzen DataSets, sodass auch
    die aktuellen Daten im Dataset vorhanden sind.

    Also ich hab mir sowas vorgestellt wie
    clearRecursive(table) -> gibt's ja schon
    fillRecursive(table) -> da wüsste ich aber nicht wie ich das bauen soll
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „tragl“ ()

    Uih - da hab ich mal einen mördermässigen Sql-Generator gebastelt, der konnte glaub sowas: "Lade diese Datensätze, und alle untergeordneten".
    Aber müsste ich suchen, und untersuchen, ob das noch funktioniert.
    Ist glaubich in dem Monster-Projekt, von dem ich den Namen vergessen halb - iwas wie "allgemeine Lösung...." muss ja im Datenbank-Bereich sein.

    ErfinderDesRades schrieb:

    : "Lade diese Datensätze, und alle untergeordneten".

    Müsste hier ja sogar umgekehrt sein - lade alle übergeordneten Tabellen und dann diese...

    Aktuell ist's so eingestellt dass sobald ein Programm-Modul aufgemacht wird, das DataSet neu befüllt wird. Dadurch, dass die .mdb-Datei nun auf einem
    Netzlaufwerk liegt, sorgt das für ordentlich Verzögerung. Meine Hoffnung hier ist, dass das schneller abläuft wenn eben nicht das komplette DTS neu befüllt wird,
    sondern "nur" die zusammenhängenden Tabellen.

    Also wenn ich sage "öffne Standort-Stammdaten" soll er die Tabelle Standort mit allen übergeordneten Tabellen neu einlesen. Das können u.U. auch sehr viele sein aber nicht alle ;)

    Wäre also super wenn du hier eine Lösung dazu hättest. ClearRecursive gibt es ja schon, ich brauche also noch ein FillRecursive

    Dann könnte man beim Öffnen eines Programmteils sowas hier veranstalten:

    VB.NET-Quellcode

    1. Private Sub open()
    2. refreshData(dts.Standort) : Formöffnen
    3. End Sub
    4. Private Sub refreshData(dt As DataTable)
    5. ClearRecursive(dt)
    6. FillRecursive(dt)
    7. End Sub
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    Wäre also super wenn du hier eine Lösung dazu hättest. ClearRecursive gibt es ja schon, ich brauche also noch ein FillRecursive
    Die Analogie passt nicht ganz, denn ClearRecursive löscht untergeordnete Datensätze - du willst aber übergeordnete (Tabellen) Reloaden.
    Nee - sowas hab ich noch nicht.
    Und ist richtig - das muss auch rekursiv ermittelt werden, welches die übergeordneten Tabellen einer Tabelle sind.

    Aber dassis auch recht gruselig, weil wenn du alle übergeordneten Reloadest, dann musst du dafür alle deren untergeordneten Tabellen löschen - mithin wohl ungefähr das komplette Dataset.

    ErfinderDesRades schrieb:

    dann musst du dafür alle deren untergeordneten Tabellen löschen - mithin wohl ungefähr das komplette Dataset.

    simmt, soweit hab' ich nicht gedacht - dann kann ich ja auch gleich das ganze DTS reloaden. OK dann müssen die Leute damit
    leben dass es erstmal länger dauert beim Öffnen von Programmteilen.

    Dann zum nächsten Problem: ;)


    hier benenne ich einen Standort um - wird sich also auf einige untergeordnete Einträge in diversen Tabellen auswirken.
    Trotz der Meldung werden alle zusammenhängenden Datensätze aber korrekt geändert. Warum dann die Meldung?

    LG
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Da alles nun läuft, dacht' ich mir bevor es hier langweilig wird komm ich ma mit dem nächsten Dingen :D

    seit Umstellung auf DB wird mir nicht mehr "0", sondern "0,0000" bei Decimal-Feldern angezeigt.
    In der Datenbank musste ich die Feldeinstellung auf "Währung" setzen damit meine Werte überhaupt übertragen wurden


    Was kann ich tun, damit's wieder schöner aussieht? Dezimalstellenanzeige in der DB auf 2 ändern bringt leider nix
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    musste mal überprüfen, was für Werte aus der DB eingelesen werden.
    Dem Aussehen nach könnten das sehr sehr kleine Werte sein, aber eben nicht 0.
    Kann man sicher am Binding fixen - da kann man irgendwie einen Format-String angeben (grad vergessen wie).

    (Aber scheint eh buggy zu sein, wie Access da mit Fliesskommazahlen umgeht - das war ja schon ein kranker Workaround, überhaupt von Decimal auf Currency umzusteigen)
    Spoiler anzeigen

    ErfinderDesRades schrieb:

    Dem Aussehen nach könnten das sehr sehr kleine Werte sein, aber eben nicht 0.

    Doch, die Werte sind 0 - das weiß ich weil ich dem DGV ja gesagt hab': blende alles was 0 als Value hat aus. ;)

    Vielleicht muss ich mir da noch'n Textboxbehaviour bauen (dctb = DecimalTextbox) hab ich ja schon, da kann ich sowas bestimmt mit abfangen. muss ich ma testen nachher

    Edit: Jo, das war's doch schon gewesen -> Zeile 5 ^^

    VB.NET-Quellcode

    1. Public Sub RegisterDecimalInput(ParamArray decimalTextBoxes As TextBox())
    2. For Each dctb In decimalTextBoxes
    3. AddHandler dctb.KeyDown, AddressOf dctb_KeyDown
    4. AddHandler dctb.Enter, AddressOf dctb_Enter
    5. If dctb.Text = "0,0000" Then dctb.Text = "0"
    6. Next
    7. End Sub


    Edit2: Oder auch nicht - auf ner anderen Form tut's das nicht, da sind die Textboxen in nem Tabcontrol auf der 2. Seite - die akualisiert er scheinbar nicht.
    Schade


    Lag' daran, dass in der DB "Currency" eingstellt war. Habe nun auf "Double" geändert, weil ich nur 2 nachkommastellen brauche - nu läufts

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    ErfinderDesRades schrieb:

    vermutlich führt die DB eine ChildRow-Umbenennung aus.

    Ich hätt' da die nächste Parallelitätsverletzung, trotz deiner neuen OleDbPersistance..... ;(
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Spoiler anzeigen
    .. uuuund das nächste Problem (vermutlich steh' ich aufm Schlauch aber es will nicht klappen):

    Mit der folgenden Sub lass' ich mir aus ner Bestellung die Positionen (geliefert <> bestellt) in eine TempTable packen, die soll vorher gecleared werden (Zeile 7)
    Das Clearen geht schonmal nicht (code läuft durch aber in der Datenbank stacken sich die TempRows bis ins Unendliche..)

    Dann kann ich in dlgNeueLieferung auswählen, welche Positionen mit welcher Menge geliefert wurden. Beim Verlassen des
    Dialogs werden dann entsprechend die TempRows in die Tabelle PosLieferung übertragen (Zeile 32) (soweit die Theorie) - in der Datenbank is leider nix zu finden...
    an anderen Stellen klappt's mit Clear und AddXYRow - hab ich was übersehen?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub neueLieferung()
    2. Dim bestellNr = bsBestellung.At(Of BestellungRow).ID
    3. Dim offenePos = Dts.Position.Where(Function(x) x.BestellID = bestellNr AndAlso x.geliefertMenge <> x.Menge)
    4. If Not offenePos.Count = 0 Then
    5. Using dlg As New dlgNeueLieferung
    6. Dts.Register(dlg, False)
    7. Dts.PosTemp.Clear()
    8. dlg.IDTextBox.Text = "NEU"
    9. dlg.ExpBestellNrTextBox.Text = bestellNr.ToString
    10. For Each rwOffen In offenePos
    11. Dts.PosTemp.AddPosTempRow(rwOffen.ID,
    12. rwOffen.expMitarbeiterName,
    13. rwOffen.expArtikelnummer,
    14. rwOffen.expArtikelBezeichnung,
    15. rwOffen.expArtikelMerkmal,
    16. rwOffen.expArtikelVE,
    17. rwOffen.Zusatz,
    18. rwOffen.Info,
    19. rwOffen.expLieferant,
    20. rwOffen.Menge - rwOffen.geliefertMenge,
    21. rwOffen.Menge,
    22. rwOffen.geliefertMenge)
    23. Next
    24. If dlg.ShowDialog() = DialogResult.OK Then
    25. Dim lt = Date.Parse(dlg._lt)
    26. Dim lsNr = dlg._lsNr
    27. Dim bemerkung = dlg._bemerkung
    28. 'Dim idCounter = -1 'Dts.PosLieferung.Count + 1
    29. For Each rwTemp In Dts.PosTemp
    30. Dim rwPos = Dts.Position.First(Function(x) x.ID = rwTemp.PosID)
    31. Dim geliefert = rwPos.geliefertMenge
    32. Dts.PosLieferung.AddPosLieferungRow(-1, lt, lsNr, bemerkung, rwPos, rwTemp.Menge, Date.Now, "", "")
    33. rwPos.geliefertMenge = geliefert + rwTemp.Menge
    34. Next
    35. End If
    36. End Using
    37. setBestellStatus()
    38. Dts.SaveDts()
    39. Else
    40. msgInformation("Es wurden bereits alle Positionen geliefert.")
    41. End If
    42. End Sub


    auch erledigt. mit der aktuellsten Version von hier klappt das ...

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „tragl“ ()

    @ErfinderDesRades:

    Ich nochmal. Also, es läuft ja mittlerweile alles wie es soll.
    Allerdings dauert das Befüllen des DataSets aus der Datenbank (weil Netzlaufwerk) verdammt lange - und ich mache mir sorgen,
    dass es noch länger dauert, je mehr Daten in der Datenbank sind.

    Jetzt ist es aber so, dass aktuell ALLE Daten aus der Datenbank in's DataSet und somit auch in den Arbeitsspeicher geladen werden. Die User brauchen aber nur einen Bruchteil davon (gefilterte Daten).
    Gibt es eine saubere Möglichkeit beim Öffnen eines Programm-Moduls nur die geforderten Daten zu laden (das geht ja in jedem Fall) und trotzdem
    das DataSet weiter zu nutzen? Also quasi statt einem BindingSource-Filter einen "LadeDatenAusDatenbank"-Filter nutzen.

    Also ich stelle mir das so vor:

    Beim Öffnen der Anwendung werden alle relevanten Daten geladen,
    z.B. Tree, User
    sowas wie Dts.Fill(Tree, User)

    Beim Öffnen eines Anwendungsteils (z.B. Urlaubsplanung) brauchen dann nur die Anwendungsdaten erhalten bleiben
    z.B. Mitarbeiterdaten(gefiltert nach Benutzerrechten), Urlaubsplandaten(gefiltert nach Benutzerrechten)
    sowas wie Dts.Fill(Mitarbeiter, Urlaubsplan)

    etc.

    Laut meinem Kenntnisstand sollte es ja auch kein Problem sein, dass ich z.B. einen Mitarbeiter umbenenne und nicht alle abhängigen Tabellen im DataSet gefüllt sind - denn die
    Datenbank ändert das ja dann selbstständig. Ist das korrekt?

    Also ich ändere einen Mitarbeiternamen - in der Urlaubsplanung die gerade geöffnet ist ändert sich der Name auch (weil im DataSet geladen) und in den
    Arbeitszeiten z.B. ändert sich der Name dann beim Speichern in der Datenbank (zu dem Zeitpunkt nicht im DataSet geladen)

    Ich hoffe du verstehst was ich meine ;) - damit möchte ich die Ladezeiten extrem verkürzen, aktuell macht es keinen Spaß damit zu arbeiten leider.
    Die DataSet-Methode möchte ich in jedem Fall gerne beibehalten (ist extrem einfacher im Code :P mit typisierten Dingen zu arbeiten)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Hi Tragl,

    wieviele Clients werden dein Programm nutzen oder vielmehr auf dein Server zugreifen um Daten zubearbeiten?
    hier Lesestoff zu Client/Server
    ip-insider.de/was-ist-das-client-server-modell-a-940627/

    dein programm wird meiner Meinung nach mit Max 5-8 Leuten funktioniern, danach wird es eng.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Kasi“ ()

    Aktuell sind es 5-6 Leute aber warum soll das eng werden? Die Ladezeit erhöht sich nur mit den Datenmengen in der DB. Deshalb möchte ich das verkürzen. Mit Client<-> Server bin ich vertraut aber wie schon erwähnt hab ich aktuell keine andere Möglichkeit als Access-dB-Datei auf dem Netzlaufwerk. Erst wenn die Anwendung offiziell genehmigt wird steht mir alles offen. Und dann wird die auch von 50-60 Usern genutzt. Ich brauche aber jetzt für die Testphase eine Übergangslösung
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    Mit Client<-> Server bin ich vertraut


    das ganze wiederspricht eine Client/Server Anwendung

    wenn ich einen neuen Kunden anlegen will, schickt der Client die Daten zum Server, vom Server will ich
    vielleicht die neue Kundennummer zurück haben oder eine Bestätigung
    das er Erfolgreich angelegt wurde.

    genauso könnte der Server dir mitteilen das Kunde X mit der Kundennummer 001 zur Zeit von User Xyz
    seit 13:10 Uhr in Bearbeitung ist und dieser Datensatz nicht auswählbar ist

    aber mit dieser Aussage ....
    jetzt ist es aber so, dass aktuell ALLE Daten aus der Datenbank in's DataSet und somit auch in den Arbeitsspeicher geladen werden. Die User brauchen aber nur einen Bruchteil davon (gefilterte Daten).

    ..kommst du langsam dahinter

    Kasi schrieb:

    das ganze wiederspricht eine Client/Server Anwendung

    Ich betreibe selbst diverse Server, von daher ist mir ganz sicher bewusst wie sich Client <-> Server-Anwendungen verhalten.

    tragl schrieb:

    aber wie schon erwähnt hab ich aktuell keine andere Möglichkeit als Access-dB-Datei auf dem Netzlaufwerk

    bedeutet: Die Anwendung habe ich geschrieben und entwickelt (mit Hilfe aus dem Forum hier). Wir sind ein Konzern, da lässt man nicht mal eben so jede Anwendung rein - besprochen wurde mit meinen Vorgesetzten, dass wir die Anwendung
    mit einer Handvoll Leute ausgiebig testen und ausprogrammieren. Erst dann geht der Vorgang an die IT-Abteilung - die prüfen dann alles und dann bekomme ich Möglichkeiten alles über Server laufen zu lassen.
    Dann bekomme ich einen SQL-Server zur Verfügung gestellt und meine Anwendung wird auch ab dann mit über die Citrix-Landschaft veröffentlicht (was das ganze auch mit Datenbank-Datei schon erheblich perfomanter machen würde).

    Ich hoffe, das war nun verständlich genug ;)
    Ich brauche für jetzt also zwingend eine Lösung, die mit Datenbankdatei auf Netzlaufwerk funzt - und da fällt mir aktuell nix anderes zu ein als nur die Daten in's DataSet zu laden, die ich auch wirklich brauche - bzw.
    die der User zu dem Zeitpunkt auch wirklich benötigt. Da eine funktionierende Berechtigungs-Steuerung einprogrammiert ist, sollte das normal kein Problem darstellen. Auch RecordLocking habe ich als primitive
    Variante (Timestamp wird zur Bearbeitung auf 3 Stunden im Voraus gestellt, nach Bearbeitung wieder auf Date.Now) schon drin und das funktioniert auch ausreichend.

    Edit: Falls du damit meinst, dass ich die Anwendung einmal als Client- und einmal als Serveranwendung bereitstellen sollte: vergiss' das wieder ;) ein Datenbank-Server im
    Backend genügt hierfür vollkommen.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    du kannst Instanzen von z.b. Kunden Laden statt alle Kundendaten laden
    dies würde bedeuten das eine andere Programmierung nötig ist = eine Kunden Klasse

    hier eine einfache variante
    du brauchst zwei forms

    Form1:

    VB.NET-Quellcode

    1. 'in Form1:
    2. '1x Button
    3. '1x Listview
    4. Public Class Form1
    5. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    6. 'Server mit SQL anfragen:
    7. 'Listview oder DGV mit einer SQL-Query befüllen (gefilterte Daten) die Kunden;Aufträge;Stammdaten oder
    8. 'was auch immer ???
    9. With ListView1
    10. .View = View.Details
    11. .LabelEdit = False
    12. .HideSelection = False
    13. .GridLines = True
    14. .FullRowSelect = True
    15. .Columns.Add("KundenID", 80)
    16. .Columns.Add("Firma", 100)
    17. .Columns.Add("Locked By", 100)
    18. .Columns.Add("Locked since", 120)
    19. End With
    20. 'gefilterte Daten aus Kundenstammdaten....
    21. lvwAddItem(ListView1, "001", "Behrent", "", "")
    22. lvwAddItem(ListView1, "002", "Becker", "", "")
    23. lvwAddItem(ListView1, "003", "Bosbeck", "", "")
    24. lvwAddItem(ListView1, "004", "Biedermann", "", "")
    25. 'etc....
    26. 'disable Button1 noch wurde nix selektiert
    27. Button1.Enabled = False
    28. 'User Information verwenden oder ???
    29. Debug.Print(Microsoft.Win32.Registry.CurrentUser.Name.ToString)
    30. Debug.Print(Environment.UserName.ToString())
    31. Debug.Print(SystemInformation.UserName)
    32. Debug.Print(System.Security.Principal.WindowsIdentity.GetCurrent().Name)
    33. End Sub
    34. Public Sub lvwAddItem(ByVal lvw As ListView, ByVal ParamArray Text() As String)
    35. 'Source von : https://www.vbarchiv.net/tipps/tipp_2010-listview-elegant-fuellen.html
    36. With lvw.Items
    37. .Add(New ListViewItem(Text))
    38. End With
    39. End Sub
    40. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    41. For Each lvItem As ListViewItem In ListView1.SelectedItems
    42. 'SQL Update hier und in Datenbank schreiben
    43. 'Locked by User = ?
    44. 'Locked since = Now()
    45. 'Record is jetzt gelockt
    46. lvItem.SubItems(2).Text = "Tragl"
    47. ' lvItem.SubItems(2).Text = Environment.UserName.ToString()
    48. lvItem.SubItems(3).Text = Now
    49. Next
    50. 'die Form die geöffnet werden soll
    51. 'hier nur ein Bsp. zur Ansicht
    52. Dim frm As New Form2
    53. For Each lvItem As ListViewItem In ListView1.SelectedItems
    54. frm.Text = "Kunde :" & lvItem.SubItems(0).Text
    55. frm.TextBox1.Text = lvItem.SubItems(0).Text
    56. frm.TextBox2.Text = lvItem.SubItems(1).Text
    57. frm.Label1.Text = lvItem.SubItems(2).Text
    58. frm.Label2.Text = lvItem.SubItems(3).Text
    59. Next
    60. 'normalerweise Me.Hide()
    61. 'Form2.Show nur um anzuzeigen was der nächste User zusehen bekommt
    62. frm.Show()
    63. End Sub
    64. Private Sub ListView1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListView1.SelectedIndexChanged
    65. For Each lvItem As ListViewItem In ListView1.SelectedItems
    66. If lvItem.SubItems(2).Text > "" Then
    67. 'disable Button weil User "Xyz" diesen Datensatz in Bearbeitung hat
    68. Button1.Enabled = False
    69. Else
    70. Button1.Enabled = True
    71. End If
    72. Next
    73. End Sub
    74. End Class


    Form2:

    VB.NET-Quellcode

    1. Public Class Form2
    2. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    3. 'close Instanz
    4. Me.Close()
    5. End Sub
    6. Private Sub Form2_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    7. 'Server mitteilen das Client fertig ist
    8. 'hier Sql um Datensatz wieder Freizuschalten
    9. End Sub
    10. End Class





    bei mir können Clients höchsten 4 Instanzen öffnen