Datenzugriff mit ADO.NET, Teil 2

Lies in den Artikel rein und unten bekommst Du ein unschlagbares Angebot!

Im ersten Teil unserer Artikelreihe zum Thema ADO.NET haben wir uns angesehen, wie Sie mit dem DataReader-Objekt auf die Daten einer Tabelle zugreifen und wie Sie mit dem Command-Objekt Aktionsabfragen durchführen können. Im vorliegenden Teil sehen wir uns an, wie Sie mit dem DataAdapter-Objekt auf die Daten einer Datenbank zugreifen und das DataSet- und das DataTable-Objekt einsetzen, um verbindungslos mit den Daten einer Datenbank zu arbeiten.

Die im ersten Teil dieser Artikelreihe vorgestellten Techniken für den Zugriff auf die Daten einer Datenbank erforderten eine geöffnete Verbindung. So etwas ist in einer Desktopdatenbank etwa auf Basis von Microsoft Access kein Problem. Etwas kritischer wird es schon, wenn Sie Access in einer Mehrbenutzerumgebung einsetzen – und vor allem dann, wenn die Anwendung über das Internet über die anzuzeigenden oder zu bearbeitenden Daten zugreift. In Zeiten der Mehrbenutzer-, Internet- und mobilen Anwendungen benötigen wir daher verbindungslose Zugriffstechnologien.

Die unterstützt ADO.NET durch einige weitere Objekte, welche nach Wunsch mit den Daten einer oder mehrerer Tabellen oder Abfragen der Datenbank gefüllt werden. Der Benutzer zeigt die Daten an, löscht, bearbeitet oder erweitert sie und stößt dann den Speichervorgang für die Daten an. Daraufhin wird erneut eine Verbindung geöffnet und die geänderten Daten werden zurück in die Datenbank geschrieben. Wie dies gelingt, schauen wir uns weiter unten an. Vorher jedoch noch einige Ergänzungen zum ersten Teil dieser Artikelreihe.

Verbindungszeichenfolge einfach festlegen

Wer sich mit dem Zusammenstellen der Zeichenkette für die Verbindungszeichenfolge schwer tut und/oder Eingabefehler bei der Benennung der Parameter vermeiden will, kann auch ein Objekt namens ConnectionStringBuilder verwenden. Dieses gibt es ebenso wie die Objekte Connection, Command et cetera jeweils mit entsprechendem Präfix für die verschiedenen ADO.NET-Klassen wie System.Data.OLEDB, System.Data.ODBC, System.Data.SQL und so weiter. Da wir aktuell noch eine Access-Datenbank als Datenquelle für unsere Beispiele nutzen, heißt die entsprechende Klasse also OLEDBConnectionStringBuilder. Die folgende Prozedur erstellt ein neues Objekt auf Basis dieser Klasse und speichert es in der Variablen ConnectionStringBuilder. Die Objektvariable stellt alle für die aktuelle Verbindung möglichen Parameter als Eigenschaften zur Verfügung, sodass Sie die gewünschten Elemente einfach per IntelliSense auswählen wollen.

Im vorliegenden Fall weisen wir so den Parameter DataSource und den Parameter Provider zum Objekt ConnectionStringBuilder hinzu und stellen so die Verbindungszeichenfolge zusammen, die wir dann über die Eigenschaft ConnectionString abfragen und beim Erstellen des OLEDBConnection-Objekts verwenden können:

public static void ConnectionstringBuilder() {
     OleDbConnectionStringBuilder ConnectionStringBuilder = 
         new OleDbConnectionStringBuilder();
     ConnectionStringBuilder.DataSource = "Suedsturm.mdb";
     ConnectionStringBuilder.Provider = 
         "Microsoft.Jet.OLEDB.4.0";
     OleDbConnection cnn = new OleDbConnection(
         ConnectionStringBuilder.ConnectionString);
     try {
         cnn.Open();
         Console.WriteLine("Provider: {0}", cnn.Provider);
         Console.WriteLine("DataSource: {0}", 
             cnn.DataSource);
         Console.ReadLine();
         cnn.Close();
     }
     catch(Exception e) {
         Console.WriteLine("Fehler: {0}", e.Message);
         Console.ReadLine();
     }
}

Im Folgenden haben wir die Ausgabe einiger Eigenschaften des Connection-Objekts in eine rudimentäre Fehlerbehandlung gesteckt. Diese greift, wenn innerhalb des try-Blocks ein Fehler auftritt. Dann wird der catch-Block ausgelöst, der eine Fehlermeldung in der Konsole ausgibt. Mehr zum Thema Fehlerbehandlung lesen Sie im Artikel Fehlerbehandlung mit C#.

Verbindungszeichenfolge speichern

Wenn Sie unter Access eine Backend-Datenbank verwenden, wird der Pfad zu den verknüpften Tabellen in der Systemtabelle MSysObjects gespeichert. Sollten die verknüpften Tabellen nicht an der vorgesehen Stelle verfügbar sein, löst dies einen Fehler aus. Dies fängt man unter Access ab, indem man beim Öffnen der Datenbank prüft, ob die verknüpfte Datenbank sich am vorgesehenen Ort befindet. Falls nicht, blendet man einen Dialog ein, der dem Benutzer die Auswahl des neuen Speicherorts der Backend-Datenbank ermöglicht und verknüpft die Tabellen dann erneut.

Dieses Verhalten wollen wir unter C# für Desktop-Anwendungen ähnlich abbilden – zumindest so, dass es für den Benutzer so aussieht. Intern läuft es natürlich etwas anders: Es gibt ja keine Verknüpfung zu einer Datenbank, sondern wir verwenden eine Zeichenkette, welche den Speicherort der zu nutzenden Datenbank angibt. Zumindest in den bisherigen Beispielen (und der Einfachheit halber auch in den meisten weiteren) legen wir die Datenbank direkt im Verzeichnis der .exe-Datei ab. Spätestens, wenn Sie einmal eine Anwendung für den Mehrbenutzerbetrieb auslegen und dabei eine Datei wie eine Access-Datenbank, aber möglicherweise auch eine Excel- oder XML-Datei als Datenquelle nutzen, sollten Sie eine Möglichkeit vorsehen, das Vorhandensein der Quelldatei zu prüfen und gegebenenfalls den Speicherort der Datei neu zu ermitteln.

Hierzu besteht die erste Aufgabe darin, den Pfad zur Quelldatei an einem Ort zu speichern, der außerhalb der kompilierten Anwendung liegt. Anderenfalls müssten Sie die Anwendung ja jedes Mal neu kompilieren, wenn sich der Pfad ändert. Das können Sie bei selbst genutzten Anwendungen tun, aber sicher nicht bei solchen Anwendungen, die Sie an Kunden oder andere Benutzer weitergeben.

Unter Access hätten wir für einen solchen Zweck einfach eine lokale Optionentabelle verwendet oder alternativ eine Textdatei. Eine C#-Desktop-Anwendung jedoch enthält ja keine eigenen Tabellen zum Speichern von Daten, also müssen wir schon auf eine Art Konfigurationsdatei zurückgreifen. Es gibt jedoch gute Nachrichten: C# sieht für Desktop-Anwendungen eine Möglichkeit vor, Konfigurationsdateien zu pflegen und die enthaltenen Daten einfach per Code einzulesen und auch zu ändern.

Wie Sie die Verbindungszeichenfolge in einer Konfigurationsdatei speichern, erfahren Sie im Artikel Anwendungskonfigurationsdateien.

Der DataAdapter

Genau wie die übrigen ADO.NET-Objekte gibt es auch den DataAdapter wieder in verschiedenen Klassen. Dementsprechend heißt der DataAdapter beispielsweise SqlDataAdapter, OdbcDataAdapter oder OleDbDataAdapter – je nachdem, mit welcher Klasse Sie auf welchen Datenbanktyp zugreifen möchten.

Wir beschäftigen uns der Einfachheit halber immer noch mit unserer Access-Beispieldatenbank Suedsturm.mdb, also nutzen wir den OleDbDataAdapter. Im Folgenden reden wir jedoch neutral von DataAdapter, nur im Beispielcode finden Sie entsprechend die Objektbezeichnung OleDbDataAdapter.

Was bietet uns der DataAdapter Mit dem Command– und dem DataReader-Objekt konnten wir ja immerhin schon einmal Auswahlabfragen ausführen und die Ergebnisse vorwärts durchlaufen oder Aktionsabfragen anstoßen. Der DataReader erlaubt es aber beispielsweise nicht, einen der enthaltenen Datensätze zu ändern und die geänderte Version zurück in die zugrunde liegenden Tabellen zu schreiben. Genauso wenig, wie Sie damit durch die Datensätze navigieren können – es geht nur vorwärts.

Außerdem sind Command und DataReader Objekte, die eine Verbindung zur Datenbank benötigen. Gerade dies ist natürlich für Web-Anwendungen oder Anwendungen auf mobilen Endgeräten nicht sinnvoll, und auch für Desktop-Anwendungen in Mehrbenutzerumgebungen können nicht endlos viele Verbindungen gleichzeitig offen gehalten werden. Daher sieht ADO.NET einige Objekte vor, mit denen Sie die Daten aus der Datenbank einlesen, bearbeiten und wieder zurückschreiben können. Dabei ist nur für das Einlesen und das Zurückschreiben eine Verbindung nötig, zwischendurch wird die Verbindung getrennt.

Und hier kommt der DataAdapter ins Spiel (je nach Anwendungsfall als SqlDataAdapter, OdbcDataAdapter, OleDbDataAdapter et cetera): Er stellt die Verbindung zwischen Client und Server her, um die benötigten Daten aus den Tabellen der Datenbank in lokale Objekte zu übertragen und diese nach Änderungen wieder zurückzuschicken. Dabei stellt der DataAdapter nur Verbindungen zum Server her, wenn tatsächlich Daten bewegt werden müssen.

Connection- und Command-Objekte

Die beiden Objekte Connection und Command, die Sie bereits kennen gelernt haben, dürfen Sie jedoch nicht aus Ihrem Gedächtnis streichen: Das Connection-Objekte benötigen wir nämlich nach wie vor, auch um die Verbindungen des DataAdapters herzustellen.

Und das Command-Objekt ist immer noch gefragt, wenn es darum geht, Änderungen an den Daten einer Tabelle vorzunehmen, für die wir die vorhandenen Daten nicht erst einlesen und analysieren müssen. Eine Aktionsabfrage etwa zum Anfügen eines neuen Datensatzes oder zum Löschen oder Bearbeiten vorhandener Datensätze ist per Command-Objekt immer noch schneller, als wenn Sie die entsprechenden Tabellen über den DataAdapter in weitere Objekte laden, die Daten dort ändern und die Änderungen dann zurückschreiben.

DataTable und DataRow

Um mit den über den DataAdapter gewonnenen Daten zu arbeiten und etwa in einer Schleife alle Datensätze zu durchlaufen und anzuzeigen, benötigen Sie noch entsprechende Objekte, um diese zu speichern. Das DataTable-Objekt nimmt dabei das Ergebnis einer Tabelle oder Abfrage auf. Seine Rows-Eigenschaft erlaubt den Zugriff auf die einzelnen Datensätze. Diese können Sie wiederum mit dem DataRow-Objekt referenzieren, um gezielt auf die Inhalte zuzugreifen.

Im Gegensatz zu den Objekten Connection, Command und DataReader, die ja je nach Anforderung etwa aus einem der Namespaces System.Data.Sql, System.Data.Odbc oder System.Data.OleDb stammen, kommen die beiden Objekte DataTable und DataRow aus dem Namespace System.Data. Diesen fügen Sie also noch über folgende Anweisung zur Klasse hinzu:

using System.Data;

DataTable füllen und durchlaufen

 

Schreibe einen Kommentar