Download Sources:
Ich habe ich mir folgendes ausgedacht zur Update-Benachrichtigung: Ich habe den Download-Link im SourceCode-Austausch eingestellt, und dort hänge ich dann immer einen Post an, wenn ich ein Update verzapft habe.
Wer also an einer Update-Benachrichtigung interessiert ist, kann den Thread dort abonnieren, und erhält dadurch eine Email, wenns ein neues Update gibt.
Und dieser Thread hier bleibt erhalten fürs Tutorial und Fragen dazu, und wird nicht mit Update-Posts verunziert - die sich mit der Zeit ja u.U. ziemlich ansammeln können
Tutorial:
Es geht um ein paar Extension-Methods, dies Dataset um Befüllung und Rückspeicherung erweitern - wahlweise in eine Datenbank, oder auch direkt auf Platte.
Design-Strategie ist, einfach ans Dataset eine Connection-Property dranzumachen, und damit sind eigentlich schon alle für den Austausch mit der DB notwendigen Informationen beisammen, und das Gewurstel mit DBConnection, DBCommands, DataAdaptern, DataReadern, DbParametern ist Vergangenheit - ja es muß nicht einmal mehr Sql geschrieben werden.
Sql schreiben ist natürlich weiterhin möglich, aber nur für sehr wenige Spezialfälle noch sinnvoll. Jedenfalls beim Befüllen des Datasets wäre es gradezu dumm, auf die Unterstützung der DBExtensions zu verzichten, wennman die Dlls eingebunden hat.
Die Extensions heißen folgerichtig Fill() und Save() - wobei Fill() gibts in Varianten, die einerseits häufige Standardfälle komfortabel unterstützen, andererseits auch die gesamte Funktions-Mächtigkeit von Sql offenhalten:
Die Arbeit macht ein Objekt namens DatasetAdapter - das muß man erstellen und dem Dataset mitteilen - hiermal ein Beispiel für SqlCe:
SqlCeProviderFactory ist vielen sicher unbekannt, aber ich kann versichern: Jedes .Net-fähige Datenbanksystem stellt neben Connections, Commands und Zeugs auch eine ProviderFactory bereit, mit der man diese Sachen auch automatisch sich fabrizieren lassen kann ;).
Dazu muß man natürlich noch einen ConnectionString angeben, aber das hat man ja immer gemußt.
Und man muß entscheiden, wie mit DBConcurrency-Konflikten umgegangen werden soll, also wenn in einer MultiUserApp 2 Anwender denselben Datensatz bearbeitet haben - wessen Version soll dann letztendlich in die Datenbank?
Mit diesen 3 Angaben weiß das Dataset alles nötige, um sich selbst befüllen und abspeichern zu können, und nun: "tschüss, Sql!" - wir proggen wieder objektorientiert und mit Compiler-Unterstützung.
Und: "tschüss, DBCommand, DataAdapter, DataReader, DbParameter!" - wir kümmern uns wieder mehr um die eigentliche Programmlogik, und weniger ums Infrastruktur-Theater.
Und auch: "tschüss, TableAdapter und TableAdapterManager!" - ihr werdet ab sofort gleich wieder rausgeschmissen, sobald der Dataset-Designer ein typisiertes Dataset aus der Datenbank generiert hat (siehe dazu "Datenbank in 10 Minuten" auf Movie-Tuts).
Schon um diese bekloppten Komponenten einzusparen lohnt sich das Einbinden von DBExtensions, denn von 10000 Zeilen generierten Codes eines (eher kleinen) typisierten Datasets gehen 5000 aufs Konto dieses Adapter-Brimboriums.
Unterstützte Datenbanksysteme: Access-OleDB, SqlCe, SqlServer und MySql, SqLite.
Dahinter steht ein ganzes Monstrum an Code, am Ende sind insgesamt ca. 20 Klassen zusammengekommen, verstreut über 3 Dll-Projekte.
Die 3 Dlls beackern verschiedene Thematiken:
Also wer sich sicher ist, dasser nie was anneres proggen wird als WinForms, der kann auch alle Klassen von "GeneralHelpers" in die WinFormHelpers schieben, und hat dann eine bischen einfachere Struktur.
Und wer sich sicher ist, dasser immer nur DB-Anwendungen schreibt, der kann alles gleich in die DBExtensions packen, dann isses nurnoch eine Dll.
Und wer überhaupt nie in den Code hineingucken will, kanns auch kompilieren, und die Kompilate direkt verwenden - dann kanner nurnix mehr dran rumschrauben.
Aber ich hab mit so Helper-Dll-Projekten gute Erfahrungen gemacht: Dahinein kann man wiederverwertbaren Code packen, der gelegentlich anfällt, und dann muß man nicht in jedem neuen Projekt das "Rad neu erfinden"
Ah - die die Sample-Solution!
Es sind inzw. 5 Sample-Solutions:
Wie vlt. schon angeklungen: Zur Einschätzung des Nutzens dieses Frameworks betrachte man vor allem den einfachen Code der Sample-Projekte. Auch die bereits vorgefertigte "Abspeichern?"-Abfrage, wenn bei vorliegenden Änderungen das MainForm geschlossen wird. Die Abfrage kann mit einer Codezeile eingebunden werden:
Hier mal der gesamte(!) Code des Mainforms der SqlCe-Lösung mit den 2 Forms:
erklärungsbedürftig nur die eine zeile im Flow-Code-Design:
Flow-Design ist kompakt, und dabei ebensogut oder besser verständlich wie untereinandergeschriebener Code - man gehe einfach alle Methoden von links nach rechts durch:
Ich werde die anneren Forms auch noch besprechen, den Code der Dlls aber nicht - das würde zu umfangreich.
Aber ich freue mich über Nachfragen jeder Art, also was die Benutzung der Dlls angeht, aber auch ihre Funktionsweise im allgemeinen, oder auch von CodeFragmenten.
Eiglich ists eher eine Prä-Beta-Release, also die Dlls sind überwiegend unkommentiert, und mehr Tests als die gegebenen Samples habich noch garnet gemacht. Ich hoffe nämlich auch auf eure Mitarbeit, also einerseits natürlich BugReports, um die Qualität zu verbessern, aber auch Nachfragen, sodasses eine Art "lebendiges Tutorial" sein könnte.
Der Download heißt sinnigerweise "AllTogether", denn ich habe alle Samples in eine Solution gestopft. Um ein anderes Sample auszuprobieren muß man es also als Startprojekt festlegen (und bei "SqlCeSamples" hat man noch die 2 Optionen bezüglich des StartFormulars).
Aber innerhalb der einzelnen Sample-Ordner sind auch spezifische Solution-Dateien, über die man jedes Sample einzeln öffnen kann. So sieht man deutlich, wie die datenbank-basierten Samples alle 3 Dlls einbinden, während "DatasetOnly" mit "nur" 2 Helper-Dlls auskommt.
Bisherige History:
Ich habe ich mir folgendes ausgedacht zur Update-Benachrichtigung: Ich habe den Download-Link im SourceCode-Austausch eingestellt, und dort hänge ich dann immer einen Post an, wenn ich ein Update verzapft habe.
Wer also an einer Update-Benachrichtigung interessiert ist, kann den Thread dort abonnieren, und erhält dadurch eine Email, wenns ein neues Update gibt.
Und dieser Thread hier bleibt erhalten fürs Tutorial und Fragen dazu, und wird nicht mit Update-Posts verunziert - die sich mit der Zeit ja u.U. ziemlich ansammeln können
Tutorial:
Es geht um ein paar Extension-Methods, dies Dataset um Befüllung und Rückspeicherung erweitern - wahlweise in eine Datenbank, oder auch direkt auf Platte.
Design-Strategie ist, einfach ans Dataset eine Connection-Property dranzumachen, und damit sind eigentlich schon alle für den Austausch mit der DB notwendigen Informationen beisammen, und das Gewurstel mit DBConnection, DBCommands, DataAdaptern, DataReadern, DbParametern ist Vergangenheit - ja es muß nicht einmal mehr Sql geschrieben werden.
Sql schreiben ist natürlich weiterhin möglich, aber nur für sehr wenige Spezialfälle noch sinnvoll. Jedenfalls beim Befüllen des Datasets wäre es gradezu dumm, auf die Unterstützung der DBExtensions zu verzichten, wennman die Dlls eingebunden hat.
Die Extensions heißen folgerichtig Fill() und Save() - wobei Fill() gibts in Varianten, die einerseits häufige Standardfälle komfortabel unterstützen, andererseits auch die gesamte Funktions-Mächtigkeit von Sql offenhalten:
- Dataset.Fill(ParamArray Tables)
löscht das gesamte Dataset und führt ein Reload durch. Über die optionalen Tables kann man bewirken, dass nicht alle Tabellen befüllt werden, sondern nur die angegebenen. - DataTable.Fill(conditions As String, ParamArray values() As Object)
Reload einer einzelnen Tabelle. (Aus datenbänkerischer Logik folgt, dass die untergeordneten Tabellen mit-gelöscht werden.)
Mittels conditions kann eine "halbe" Sql-Query angegeben werden. Nämlich alles mögliche nach dem SELECT-Abschnitt. Man kann dort WHERE - Klauseln angeben, aber auch INNER-JOINs formulieren - mw. auch ORDER-/GROUP BY - habichnix dagegen
Nur den SELECT-Abschnitt generiert die DataTable selber, denn ihre Spaltenstruktur gibt ja genauestens vor, welche DB-Spalten selektiert werden müssen.
conditions kann auch Parameter spezifizieren - Platzhalter-Zeichen hierfür ist '?'. In dem Falle sind die zu übertragenden Parameter-Werte über das values - Argument angegeben. Also bei Bedarf unterstützt DBExtensions den vollen Sql-Leistungsumfang, der auf Datasets anwendbar ist, und zwar auf ordentliche Weise: nämlich unter Verwendung passender DBParameter. - parentRow.FillChildTables(dataTables(), conditions, values())
Eine ParentRow kann nun ihre untergeordneten DataTables befüllen. Angenommen eine KundeRow - die kann nun die BestellungTable füllen mit allen BestellungDatensätzen, die auf diesen Kunden verweisen.
Es können sogar unter-untergeordnete DataTables mit-befüllt wern, etwa die BestellDetails-DataTable kann zusätzlich zur BestellTable angegeben sein, und würde dann mit allen BestellDetails aller Bestellungen dieses Kunden befüllt.
Über conditions kann man weitere Bedingungen geltend machen, etwa einen WHERE - Abschnitt, der einen bestimmten Zeitbereich selektiert oder sowas. - Dataset.Save(frm As Form) speichert dann alle Änderungen in die DB, wobei vorher das Form auch noch validiert wird - dass nicht ungültige Eingaben übernommen werden, oder gültige Eingaben vergessen.
Die Arbeit macht ein Objekt namens DatasetAdapter - das muß man erstellen und dem Dataset mitteilen - hiermal ein Beispiel für SqlCe:
Dazu muß man natürlich noch einen ConnectionString angeben, aber das hat man ja immer gemußt.
Und man muß entscheiden, wie mit DBConcurrency-Konflikten umgegangen werden soll, also wenn in einer MultiUserApp 2 Anwender denselben Datensatz bearbeitet haben - wessen Version soll dann letztendlich in die Datenbank?
Mit diesen 3 Angaben weiß das Dataset alles nötige, um sich selbst befüllen und abspeichern zu können, und nun: "tschüss, Sql!" - wir proggen wieder objektorientiert und mit Compiler-Unterstützung.
Und: "tschüss, DBCommand, DataAdapter, DataReader, DbParameter!" - wir kümmern uns wieder mehr um die eigentliche Programmlogik, und weniger ums Infrastruktur-Theater.
Und auch: "tschüss, TableAdapter und TableAdapterManager!" - ihr werdet ab sofort gleich wieder rausgeschmissen, sobald der Dataset-Designer ein typisiertes Dataset aus der Datenbank generiert hat (siehe dazu "Datenbank in 10 Minuten" auf Movie-Tuts).
Schon um diese bekloppten Komponenten einzusparen lohnt sich das Einbinden von DBExtensions, denn von 10000 Zeilen generierten Codes eines (eher kleinen) typisierten Datasets gehen 5000 aufs Konto dieses Adapter-Brimboriums.
Unterstützte Datenbanksysteme: Access-OleDB, SqlCe, SqlServer und MySql, SqLite.
Dahinter steht ein ganzes Monstrum an Code, am Ende sind insgesamt ca. 20 Klassen zusammengekommen, verstreut über 3 Dll-Projekte.
Die 3 Dlls beackern verschiedene Thematiken:
- "GeneralHelpers": enthält lauter listigen Kleinkram, der ganz allgemein und in vielen Projekten nützlich sein kann, nicht nur Datenbank-Anwendungen. "GeneralHelpers" hat nichtmal WinForms eingebunden, also könnteman auch in Wpf-Anwendungen verwenden, ohne sich unnützen Ballast ans Bein zu binden.
- "WinFormHelpers": beschäftigt sich mit Forms und BindingSources - grad BindingSources sind ohne Extension-Methods ziemlich unangenehm zu becoden, findich.
"WinFormHelpers" löst auch das Problem redundanter Datasets, wenn man mehrere Forms hat (oder mit UserControls arbeitet).
Und es bietet auch 3 Extension an, wenn mans Dataset einfach als Xml auf Platte schreiben will:- Dataset.DataFile(path) -setzt, wohin die Xml-Datei zu speichern ist.
- + 3.: Dataset.Fill() und Dataset.Save() - grad so, als hätte man einen DatasetAdapter zu Diensten
- Dataset.DataFile(path) -setzt, wohin die Xml-Datei zu speichern ist.
- "DBExtensions": implementiert eingangs vorgestellte Funktionalität
Also wer sich sicher ist, dasser nie was anneres proggen wird als WinForms, der kann auch alle Klassen von "GeneralHelpers" in die WinFormHelpers schieben, und hat dann eine bischen einfachere Struktur.
Und wer sich sicher ist, dasser immer nur DB-Anwendungen schreibt, der kann alles gleich in die DBExtensions packen, dann isses nurnoch eine Dll.
Und wer überhaupt nie in den Code hineingucken will, kanns auch kompilieren, und die Kompilate direkt verwenden - dann kanner nurnix mehr dran rumschrauben.
Aber ich hab mit so Helper-Dll-Projekten gute Erfahrungen gemacht: Dahinein kann man wiederverwertbaren Code packen, der gelegentlich anfällt, und dann muß man nicht in jedem neuen Projekt das "Rad neu erfinden"
Ah - die die Sample-Solution!
Es sind inzw. 5 Sample-Solutions:
- "Northwind": eine "große" Access-DB (MS' Northwind-Beispiel-DB)
- "SqlCeSamples": ist eine doppelte Anwendung, mit sageUndSchreibe 11 Datensätzen ;). Wenn man als StartFormular frmIncrementalFill setzt, wird je nach angewählten Kunden nur dessen Bestellungen von der DB abgerufen. Im Ausgabefenster wird das generierte Sql protokolliert.
Wenn man hingegen mit frmTwoFormMain startet, so sieht man 2 Forms, die dasselbe Dataset präsentieren, und kann sich über die Funktion Formübergreifenden Databindings freuen - "DatasetOnly": Ich habe die Northwind-DB in ein Xml-File geschrieben, und dann das Northwind-Projekt ohne Access und ühaupt ohne die DBExtensions-Dll implementiert. Ich kann keinen Unterschied bei der Lade-Geschwindigkeit feststellen, aber der Code ist noch einfacher, und das Konzept bietet halt die genannten Vorteile, was Entwicklung und Portabilität angeht. Etwa Änderungen am Datenmodell sind im Dataset-Designer einfacher umgesetzt, da man nicht zusätzlich die dahinterliegende Datenbank überarbeiten muss.
- "SqlServerTest": fast dieselbe App wie "Northwind", nur DBProvider ist halt SqlServer
- "MySqlTester": diese App ist nicht lauffähig. Ich habe sie anhand einer im Internet liegenden MySql-DB entwickelt, aber im Download das Passwort entfernt.
Wie vlt. schon angeklungen: Zur Einschätzung des Nutzens dieses Frameworks betrachte man vor allem den einfachen Code der Sample-Projekte. Auch die bereits vorgefertigte "Abspeichern?"-Abfrage, wenn bei vorliegenden Änderungen das MainForm geschlossen wird. Die Abfrage kann mit einer Codezeile eingebunden werden:
Hier mal der gesamte(!) Code des Mainforms der SqlCe-Lösung mit den 2 Forms:
VB.NET-Quellcode
- Imports System.Data.Common
- Imports System.Data.SqlServerCe
- Public Class frmMainM_N
- Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
- Dim adp = New DatasetAdapter( _
- SqlCeProviderFactory.Instance, My.Settings.BestellungenConnectionString, _
- ConflictOption.OverwriteChanges)
- Me.AlignOnTop.BestellungenDts.Register(Me).Adapter(adp).Fill()
- AddHandler Me.FormClosing, BestellungenDts.HandleFormClosing
- End Sub
- Private Sub MenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) _
- Handles ReloadToolStripMenuItem.Click, SaveToolStripMenuItem.Click
- Select Case True
- Case sender Is ReloadToolStripMenuItem
- BestellungenDts.Fill()
- Case sender Is SaveToolStripMenuItem
- BestellungenDts.Save(Me)
- End Select
- End Sub
- End Class
erklärungsbedürftig nur die eine zeile im Flow-Code-Design:
Flow-Design ist kompakt, und dabei ebensogut oder besser verständlich wie untereinandergeschriebener Code - man gehe einfach alle Methoden von links nach rechts durch:
- .AlignOnTop: dockt das Form am oberen Bildschirmrand, dabei die ganze Bildschirmbreite nutzend. Ist oft fürs Debuggen nett, weil da kann man unten das Ausgabefenster sehen (hier aber grad nicht erforderlich).
- .BestellungenDts.Register(Me): löst das RedundanzProblem, wenn mehrere Forms dieselben Daten präsentieren sollen: Das Form wird mit Reflection regelrecht abgegrast, und alle gefundenen Datasetse werden durch ein einziges ausgetauscht, und alle BindingSources werden auf dieses einzige umgestöpselt.
- .Adapter(adp): teilt dem Dataset seinen DatasetAdapter mit
- .Fill(): Dataset komplett befüllen.
Ich werde die anneren Forms auch noch besprechen, den Code der Dlls aber nicht - das würde zu umfangreich.
Aber ich freue mich über Nachfragen jeder Art, also was die Benutzung der Dlls angeht, aber auch ihre Funktionsweise im allgemeinen, oder auch von CodeFragmenten.
Eiglich ists eher eine Prä-Beta-Release, also die Dlls sind überwiegend unkommentiert, und mehr Tests als die gegebenen Samples habich noch garnet gemacht. Ich hoffe nämlich auch auf eure Mitarbeit, also einerseits natürlich BugReports, um die Qualität zu verbessern, aber auch Nachfragen, sodasses eine Art "lebendiges Tutorial" sein könnte.
Der Download heißt sinnigerweise "AllTogether", denn ich habe alle Samples in eine Solution gestopft. Um ein anderes Sample auszuprobieren muß man es also als Startprojekt festlegen (und bei "SqlCeSamples" hat man noch die 2 Optionen bezüglich des StartFormulars).
Aber innerhalb der einzelnen Sample-Ordner sind auch spezifische Solution-Dateien, über die man jedes Sample einzeln öffnen kann. So sieht man deutlich, wie die datenbank-basierten Samples alle 3 Dlls einbinden, während "DatasetOnly" mit "nur" 2 Helper-Dlls auskommt.
Bisherige History:
- 1.10.11: first posted
- 7.11.11:
- Sql-Generierung sehr optimiert
- Code-Design geändert, von DataTable.FillByParentrow(parentRow) auf parentRow.FillChildTables(tables())
- Unterstützung für SqlServer zugefügt
- Sql-Generierung sehr optimiert
- 8.11.11: Bugfix der DataTable.Fill(conditions, values()) - Extension-Methode
- 12.11.11: Unterstützung auch von Sql-Funktionen, wie SUM(), BETWEEN, IN() und sowas
Dieser Beitrag wurde bereits 21 mal editiert, zuletzt von „ErfinderDesRades“ ()