Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.
An Abfragen aller Art (SQL-Queries, Stored Procedures) führt beim Programmieren von Datenbankanwendungen kein Weg vorbei. Unter ADO.NET werden für alle Datenbankabfragen Command-Objekte benutzt, die zentraler Bestandteil der jeweiligen .NET-Datenprovider sind.
Wie bei fast allen anderen ADO.NET-Objekten, bieten sich auch zum Erzeugen eines Command-Objekts verschiedene Konstruktoren an. Eine übliche Vorgehensweise ist es, die gewünschte Abfrage neben dem zuvor angelegten Connection-Objekt an den Konstruktor der Klasse zu übergeben:
Dim cmd As New Command(sql As String, conn As Connection)
Das so erzeugte und initialisierte Command-Objekt kann dann z.B. an den Konstruktor der DataAdapter-Klasse weitergereicht werden, um letztendlich ein DataSet zu füllen.
Aber es geht auch ohne DataAdapter und DataSet, denn um SQL-Anweisungen direkt gegen die Datenquelle zu fahren, kann eine der Execute-Methoden (ExecuteNonQuery, ExecuteReader, ExecuteScalar) aufgerufen werden.
Auch mit Hilfe der CreateCommand-Methode eines Connection-Objekts können Sie ein Command-Objekt erzeugen. Damit ersparen Sie sich etwas Schreibarbeit.
Wir werden uns hier nur auf eine knappe Darstellung der wichtigsten Eigenschaften beschränken.
Beide Eigenschaften werden üblicherweise bereits im Konstruktor übergeben (siehe oben), man kann Sie aber auch separat zuweisen.
Um festzulegen, wie lange die Ausführung einer Abfrage maximal dauern darf, können Sie der Command-Timeout-Eigenschaft einen Wert in Sekunden zuweisen (Standardwert = 30 Sekunden).
Sollte eine Abfrage dennoch zu lange dauern, so können Sie diese innerhalb einer asynchronen Umgebung mit Hilfe der Cancel-Methode abbrechen.
Zum Ausführen einfacher Datenbankabfragen (wie im obigen Beispiel), können Sie auf das explizite Erzeugen eines Command-Objekts verzichten, denn man kann den SQL-String auch direkt dem DataAdapter als Parameter übergeben.
Mit der CommandType-Eigenschaft definieren Sie die auszuführende Operation. Mittels der gleichnamigen Enumeration stehen drei Möglichkeiten zur Verfügung:
Text (Standardwert)
Hier können Sie eine frei definierbare SQL-Abfrage übergeben
StoredProcedure
Hier soll eine in der Datenbank gespeicherte Prozedur bzw. Auswahlabfrage aufgerufen werden
TableDirect
Hier wird direkt der Name einer Tabelle angegeben (entspricht SELECT * FROM <Tabellenname> )
Diese Eigenschaft dürfte für den Einsteiger zunächst nur von untergeordnetem Interesse sein. Der Profi weiß aber, dass man mit UPDATE- und INSERT-Abfragen nicht nur Datensätze in der Datenquelle ändert, sondern dass auch Ausgabeparameter oder sogar Datensätze zurückgegeben werden können.
Typisch ist dies bei so genannten Stapel- bzw. Batch-Abfragen, denn hier können Sie z.B. Ausgabeparameter bzw. Datensätze aus der Datenbank abrufen, sofort nachdem der DataAdapter eine Aktualisierung entsprechend seiner UpdateCommand- oder InsertCommand-Eigenschaft durchgeführt hat.
Mittels der UpdatedRowSource-Eigenschaft legen Sie fest, ob und wie das Command-Objekt die von der Datenquelle zurückgelieferten Parameter bzw. Zeilen in das DataSet-Objekt einfügen soll.
Die UpdateRowSource-Enumeration stellt dazu entsprechende Konstanten bereit:
Tabelle 4.7 Die Mitglieder der UpdatedRowSource-Enumeration
UpdatedRowSource-Mitglied | Beschreibung |
|---|---|
Both | Sowohl die erste zurückgegebene Zeile als auch die Ausgabeparameter werden der geänderten Zeile im DataSet zugeordnet (Standard) |
FirstReturnedRecord | Nur die Daten in der ersten zurückgegebenen Zeile werden der geänderten Zeile im DataSet zugeordnet |
OutputParameters | Nur die Ausgabeparameter werden der geänderten Zeile im DataSet zugeordnet |
None | Alle zurückgegebenen Parameter oder Zeilen werden ignoriert |
Diese Methode setzen Sie vor allem ein, um Aktionsbefehle auf Basis von UPDATE, INSERT oder DELETE direkt gegen die Datenbank auszuführen (also ohne Verwendung von DataAdapter und DataSet). Rückgabewert ist hier die Anzahl der betroffenen Datensätze (sonst –1).
Weitere Möglichkeiten für Aktionsbefehle sind die Abfrage der Struktur einer Datenbank oder das Erstellen von Datenbankobjekten wie z.B. Tabellen (CREATE TABLE ...).
Auf Basis eines SELECT-Befehls erzeugt diese Methode ein DataReader-Objekt. Ein Instanziieren des Data-Readers mittels New-Konstruktor entfällt deshalb.
Rückgabewert dieser Methode ist das Objekt der ersten Spalte der ersten Zeile aus der Menge der zurückgegebenen Datensätze.
Generell eignet sich die ExecuteScalar-Methode des Command-Objekts für alle Abfragen, bei denen man nur an der Rückgabe eines einzigen Wertes interessiert ist.
Besonders vorteilhaft kann man ExecuteScalar zur Ausführung von Aggregatfunktionen verwenden, was weniger Aufwand erfordert als die Anwendung der ExecuteReader-Methode.
In einfachen Codebeispielen stellt man häufig fest, dass der Aufruf der Dispose()-Methode auf SqlConnection- und SqlCommand-Objekten fehlt[26]. Auch wird der Datenzugriffscode meist nicht in Try-Finally-Blöcke eingerahmt.
Das Problem ist, dass SqlConnection und SqlCommand die Schnittstelle IDisposable implementieren, d.h., es können auch Ressourcen aus nicht verwaltetem (unmanaged) Code zu bereinigen sein. Als Programmierer müssen Sie dann unter allen Umständen absichern, dass Dispose auf diesen Objekten aufgerufen wird, nachdem die Arbeit mit ihnen beendet ist. Weil bei Nichtverfügbarkeit der Datenbank immer ein Fehler auftreten kann, sollten Sie den Aufruf von Dispose auch für diesen Fall gewährleisten.
Das Problem lässt sich elegant mittels Using-Schlüsselwort lösen, welches Ihnen lästige Schreibarbeiten abnimmt, denn intern wird automatisch ein Try-Finally-Block um das entsprechende Objekt generiert und beim Beenden wird für das Objekt Dispose aufgerufen.