DatasetOnly: DB-Programmierung ohne Datenbank

    • VB.NET

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

      DatasetOnly: DB-Programmierung ohne Datenbank

      Relativ wenig bekannt zu sein scheint die Möglichkeit, ein Dataset mit .WriteXml direkt auf Platte zu schreiben, und mit .ReadXml wieder einzulesen.
      Dabei ist das Verfahren überaus simpel, schnell, und spart Festplatte. Auch ist man unabhängig von einem Datenbanksystem (DBMS). Und man spart sich den ganzen Aufwand und die Fußfallen, die ein klassischer DB-Zugriff so mit sich bringt.
      Selbst wenn man ein DBMS verwenden will, kann das Verfahren während der Entwicklung überaus nützlich sein, denn man kann das Datenmodell viel leichter ändern und weiter-entwickeln, als wenn man immer zunächstmal die hinterlegte DB überarbeiten müsste.
      Ein DBMS kann man immer noch später hinterlegen, wenn das Datenmodell ausgereift ist. In vielen Fällen isses aber nichtmal nötig, denn Datasets bis 20MB stellen kein Speicherproblem dar, und sind schneller komplett eingelesen, als die kleinste Abfrage über einen DBMS-Provider in Anspruch nimmt. Und 20 MB Daten muß man erstmal vollkriegen ;) .
      Ich habe schon mit 400MB-Datasets gearbeitet, und zu meiner eigenen Überraschung kein Performance-Problem feststellen können (aber 400MB scheint mir wirklich bischen irre).

      Nebenbemerkung
      Hier ein Thread, wo sich Vatter und SpaceyX (letzterer sogar mit Video!) die Mühe gemacht haben, bebilderte Anleitungen zu geben, wie das Geklickse zum Einrichten eines typisierten Datasets vonstatten gehen kann: Listview_Inhalt_speichern_/_auslesen
      Weitere Filmles dazu: vier Views-Videos
      /Nebenbemerkung

      Das Projekt zeigt auch 2 (von 4) klassischen Views, die man mit Databinding zusammenkloppen kann:
      1) ParentChild-View: Aus einem DatagridView kann eine Kategorie gewählt werden - ein zweites DGV zeigt die zugehörigen AddOns (ist egal, wasses ist - sind Datensätze halt)
      2) Einzelblatt-View: bestimmte Werte sind zu lange Strings, als dass sie als Tabellenspalte angezeigt werden könnten. Diese werden in datengebundenen Textboxen angezeigt: Also man wählt im AddOn-DGV ein AddOn, und die Textboxen zeigen den Link zum AddOn und die Description (nur sinnlose Beispiele).

      Die ganze Geschichte wird unter intensiver Verwendung der vorgesehenen Designer umgesetzt: FormDesigner, DatasetDesigner und DatenFenster der IDE.
      Designer gewährleisten korrekt konfigurierte Objekte, und verstecken den eigentlich trivialen, aber sehr umfangreichen Konfigurier-Code in partialen Klassen (Endung: .Designer.vb). Das ist ein großer Vorteil, weil man sich dann im UserCode aufs Wesentliche konzentrieren kann.
      Das Sample etwa ist eine voll editierbare Datenbank-Anwendung, als UserCode sind aber nur folgende paar Zeilen erforderlich, um laden und speichern zu managen, inklusive einer intelligenten "Änderungen speichern?" - Abfrage beim Schließen des Forms:

      VB.NET-Quellcode

      1. Imports AddOnDB.DBSampleDataSet, System.IO
      2. Imports System.ComponentModel
      3. ' Parent-Child-View der Relation Category-AddOn:
      4. ' Im linken Grid kann man eine Kategorie anwählen, das rechte Grid zeigt daraufhin die dieser Kategorie zugehörigen AddOns an. Beide Tabellen sind vollständig editierbar.
      5. 'Der AddOn-View ist zusätzlich mit einem EinzelblattView ausgestattet: Da die Werte "Link" und "Description" nicht in eine Tabellenspalte passen, werden sie in datengebundenen Richtextboxen angezeigt.
      6. Public Class frmParentChild
      7. Private _DataFile As New FileInfo("..\..\DBSample.DataSet.xml")
      8. Private Sub frmParentChild_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
      9. Reload()
      10. End Sub
      11. Private Sub MenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) _
      12. Handles ReloadToolStripMenuItem.Click, SaveToolStripMenuItem.Click, TestToolStripMenuItem.Click
      13. Select Case True
      14. Case sender Is ReloadToolStripMenuItem
      15. Reload()
      16. Case sender Is SaveToolStripMenuItem
      17. Save()
      18. Case sender Is TestToolStripMenuItem
      19. Dim addOn = DirectCast(DirectCast(AddOnSource.Current, DataRowView).Row, AddOnRow)
      20. With New StringList
      21. .AddLine("hier könnte mit folgendem Datensatz was passieren:")
      22. .AddLine("Name: ", addOn.Name)
      23. .AddLine("Version: ", addOn.Version)
      24. .AddLine("Description: ", addOn.Description)
      25. .AddLine("Link: ", addOn.Link)
      26. MsgBox(.ToString)
      27. End With
      28. End Select
      29. End Sub
      30. Private Sub frmParentChild_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) _
      31. Handles Me.FormClosing
      32. If e.Cancel Then Return
      33. Dim frm = DirectCast(sender, Form)
      34. If Not DBSampleDataSet.HasChanges Then Return
      35. Select Case MessageBox.Show( _
      36. frm, "Änderungen speichern?", "Das Ende ist nahe", MessageBoxButtons.YesNoCancel)
      37. Case Windows.Forms.DialogResult.Yes
      38. Save()
      39. Case Windows.Forms.DialogResult.No
      40. 'nix tun
      41. Case Windows.Forms.DialogResult.Cancel
      42. e.Cancel = True
      43. Return
      44. End Select
      45. e.Cancel = False
      46. End Sub
      47. Private Sub Save()
      48. 'Validate überprüft alle aktuellen Control-Eingaben, und **übernimmt sie in die Datasource**. Letzteres ist wichtig, da ansonsten der User überrascht wird, weil seine letzte Eingabe gecancelt würde.
      49. 'AcceptChanges markiert alle Datensätze als übereinstimmend mit der "Datenbank" - wichtig für den Dataset.HasChanges-Test im FormClosing
      50. Me.Validate()
      51. Me.DBSampleDataSet.WriteXml(_DataFile.FullName)
      52. DBSampleDataSet.AcceptChanges()
      53. Media.SystemSounds.Asterisk.Play()
      54. End Sub
      55. Private Sub Reload()
      56. Me.DBSampleDataSet.Clear()
      57. If _DataFile.Exists() Then
      58. Me.DBSampleDataSet.ReadXml(_DataFile.FullName)
      59. Else
      60. MsgBox(String.Concat( _
      61. "Leider (noch) kein DatenFile vorhanden", Lf, _
      62. "Sie können aber trotzdem fortfahren, und eines anlegen."))
      63. End If
      64. DBSampleDataSet.AcceptChanges()
      65. End Sub
      66. End Class


      Hinweise
      • Die #Region "allgemein verwendbare Funktionalität" im SampleCode von Phonebook stellt eine verbesserte Variante für Laden, Speichern und Schließen-Abfrage dar, welche schneller lädt, und einige unerfreuliche Bugs umgeht, die in anspruchsvolleren Binding-Szenarien auftreten können (nicht müssen).

      • Tutorial: Daten Filtern und berechnete Spalten im Dataset - geht auf einen hier nicht behandelten Aspekt der "Dataset-Power" ein




      .
      Dateien
      • AddOnDB.zip

        (27,87 kB, 1.618 mal heruntergeladen, zuletzt: )

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

      Hier mal 2 Bildchen, wie ein ParentChild-View, kombiniert mit Einzelblatt-View, in diesem Falle aussieht:
      Im linken Bild ist die Kategorie "Allgemeine WindowsProgramme" ausgewählt, und von diesen Progs (es gibt da nur 2) das Prog "7Zip".
      Da der Link zur 7Zip-Site zu lang ist, um in eine DGV-Zelle zu passen, wird er eben in einer gebundenen Richtextbox angezeigt, die immer umspringt, wenn ein anderes Prog ausgewählt wird (ebenso funzt "Description" - das sind die beiden Elemente des Einzelblatt-Views).
      Das rechte Bild etwa zeigt alle Programme der Kategorie "Tool" (es gibt nur eines).
      Wie gesagt: Jeder Wert ist editier-/lösch-bar. Man kann Kategorien anlegen und Programme innerhalb von Kategorien zufügen, und laden/speichern von alledem ist auch gleich mit drin.
      Das Design des Samples ist zugegebenermaßen erbärmlich, aber die Funktionalität haut schonmal hin. Am Design mag sich verkünsteln, wer will: Farbgebung, Images, runde Ecken... - alles Geschmacksache, solange das Verständnis der eigentlichen Programm-Funktionalität nicht verbaut wird.
      Bilder
      • Shots12.Png

        21,88 kB, 811×265, 4.343 mal angesehen
      • Shots13.Png

        21,86 kB, 811×265, 3.675 mal angesehen
      Hier noch Bildchen, um zu zeigen, wie ein typisierten Dataset schon zur DesignZeit den Entwurf der DatagridViews unterstützt. Nämlich alle Spalten der DataTables sind bereits im Designer des DGVs vorhanden, und über den SmartTag kann man einen Dialog öffnen, der überaus mächtige Optionen bietet, um jede Spalte einzeln differenziert zu gestalten (SizeMode, Readonly, Formatierung von Zahlen oder Datum, Rechtsbündigkeit...)
      Bilder
      • AddOnDB00.Png

        16,05 kB, 814×295, 2.782 mal angesehen
      • AddOnDB01.Png

        46,55 kB, 843×442, 2.533 mal angesehen

      Most Primitive

      Hier ein Beispiel, wos Dataset nur für Settings verwendet wird - Databinding spielt hier keine Rolle.

      Beispiel-Anwendung ist ein MDI-Form, welches seine ChildForms abspeichert.

      Natürlich nicht die ChildForms selbst - das geht ja gar nicht - sondern die für die Anwendung signifikanten Properties werden abgespeichert, nämlich:
      • Text ................................(As String)
      • Left, Top, Width, Height .....(As Integer)
      • Erstell-Zeitpunkt ................(As DateTime)

      Dazu habich einfach ein Dataset mit einer Tabelle gebastelt, deren Datensätze genau diese Infos aufnehmen:


      Wenn mans geschafft hat, eine solche Tabelle zu creiern, hat man im weiteren nichts mehr zu tun mit speziellen Datei-Formaten wie Xml oder Ini-Dateien. Man muß auch keine Integer oder Datumse nach String konvertieren und zurück.

      Einfach: Laden und Werte verwenden
      Oder: Werte zurückschreiben und speichern

      VB.NET-Quellcode

      1. Imports System.IO
      2. Public Class frmMDI
      3. Private _DataFile As New FileInfo("..\..\Data.xml")
      4. Private Sub frmParentChild_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
      5. If Not _DataFile.Exists Then Return
      6. Me.DataDts.ReadXml(_DataFile.FullName)
      7. For Each inf In DataDts.ChildInfo ' ChildInfo: die DataTable im Dataset
      8. Dim frm As New frmToAdd
      9. With frm
      10. .RichTextBox1.Text = inf.Text
      11. .Left = inf.Left
      12. .Top = inf.Top
      13. .Width = inf.Width
      14. .Height = inf.Height
      15. .CreatedAt = inf.CreatedAt
      16. .MdiParent = Me
      17. .Show()
      18. End With
      19. Next
      20. End Sub
      21. Private Sub btAdd_Click(ByVal sender As Object, ByVal e As EventArgs) _
      22. Handles btAdd.Click
      23. With New frmToAdd
      24. .MdiParent = Me
      25. .Show()
      26. End With
      27. End Sub
      28. Private Sub Form_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) _
      29. Handles Me.FormClosing
      30. DataDts.ChildInfo.Clear()
      31. For Each frm As frmToAdd In Me.MdiChildren
      32. With frm
      33. Me.DataDts.ChildInfo.AddChildInfoRow( _
      34. .RichTextBox1.Text, .Left, .Top, .Width, .Height, .CreatedAt)
      35. End With
      36. Next
      37. DataDts.WriteXml(_DataFile.FullName)
      38. End Sub
      39. End Class


      Update:
      Habe nochn Form zugefügt, mit dem man die DataTable konventionell im DGV anzeigen kann.

      Dieses kleine Anhängsel ist vlt. deswegen interessant, weils das Konzept "Trennung von Gui und Daten" illustriert:
      Das Gui ist nur ein View, eine Sicht auf die Daten - für die Daten ists vollkommen irrelevant, ob sie nun als MDIChildren eines MDIParents angezeigt werden, oder konventionell, in einem DatagridView.

      Also um das frmMDI in Aktion zu sehen, muss mans in den Projekteinstellungen als Startform auswählen.
      Dateien
      • MostPrimitive.zip

        (27,46 kB, 825 mal heruntergeladen, zuletzt: )

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