Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 24 ADO.NET – Das Command-Objekt
Pfeil 24.1 Das »SqlCommand«-Objekt
Pfeil 24.1.1 Erzeugen eines »SqlCommand«-Objekts
Pfeil 24.1.2 Die Methode »CreateCommand« des »Connection«-Objekts
Pfeil 24.1.3 Ausführen des »SqlCommand«-Objekts
Pfeil 24.1.4 Die Eigenschaft »CommandTimeout« des »SqlCommand«-Objekts
Pfeil 24.2 Aktionsabfragen absetzen
Pfeil 24.2.1 Datensätze hinzufügen
Pfeil 24.2.2 Datensätze löschen
Pfeil 24.2.3 Datensätze ändern
Pfeil 24.2.4 Abfragen, die genau ein Ergebnis liefern
Pfeil 24.3 Das »SqlDataReader«-Objekt
Pfeil 24.3.1 Datensätze einlesen
Pfeil 24.3.2 Schließen des »SqlDataReader«-Objekts
Pfeil 24.3.3 MARS (Multiple Active Resultsets)
Pfeil 24.3.4 Batch-Abfragen mit »NextResult« durchlaufen
Pfeil 24.3.5 Schema eines »SqlDataReader«-Objekts untersuchen
Pfeil 24.4 Parametrisierte Abfragen
Pfeil 24.4.1 Parametrisierte Abfragen mit dem SqlClient-Datenprovider
Pfeil 24.4.2 Die Klasse »SqlParameter«
Pfeil 24.4.3 Asynchrone Abfragen
Pfeil 24.4.4 Gespeicherte Prozeduren (Stored Procedures)


Galileo Computing - Zum Seitenanfang

24.4 Parametrisierte Abfragen Zur nächsten ÜberschriftZur vorigen Überschrift

Die Suche nach einem bestimmten Datensatz einer Tabelle wird durch die WHERE-Klausel einer SELECT-Abfrage bestimmt:


SELECT ProductName FROM Products WHERE ProductName='Tunnbröd'

Unstrittig ist, dass die Hartcodierung dieser Abfrage weder anwender- noch praxisgerecht ist. Was ist, wenn der Anwender nicht nach dem Artikel Tunnbröd suchen möchte, sondern die Informationen über den Artikel Tofu benötigt? Die Abfrage muss allgemeiner formuliert werden, und zwar so, dass der Anwender zur Laufzeit des Programms den Artikel beliebig bestimmen kann.

Die Lösung lautet: Wir müssen eine parametrisierte Abfrage formulieren. Berücksichtigen Sie bei den folgenden Ausführungen jedoch, dass die Wahl des .NET-Datenproviders maßgeblich die Syntax des SELECT-Statements und des Programmcodes einer parametrisierten Abfrage beeinflusst.


Galileo Computing - Zum Seitenanfang

24.4.1 Parametrisierte Abfragen mit dem SqlClient-Datenprovider Zur nächsten ÜberschriftZur vorigen Überschrift

Ist die Entscheidung auf den SqlClient-Datenprovider gefallen, könnte das Statement wie folgt lauten:


SELECT * FROM Products 
WHERE ProductName = @Productname OR CategoryID = @CatID

@ProductName und @CatID sind benannte Parameter, denen das @-Zeichen vorangestellt wird. Dieses gilt jedoch nur im Zusammenhang mit dem SqlClient-Datenprovider. Die Datenprovider OleDb und Odbc unterstützen benannte Parameter nicht, sondern nur den generischen Parametermarker. Dabei handelt es sich um das Fragezeichen (?). Der Grund für diese Abweichung der Datenprovider ist sehr einfach: Während der OleDb- bzw. Odbc-Datenprovider eine datenbankunabhängige Syntax erlaubt, ist der SqlClient-Provider für den SQL Server gedacht, der benannte Parameter mit diesem Präfix unterstützt.

Die Parameter einer parametrisierten Abfrage werden vom SqlCommand-Objekt gesammelt. Dieses besitzt eine Parameters-Auflistung, der die einzelnen Parameter hinzugefügt werden. Wenn Sie den SqlClient-Datenprovider verwenden, handelt es sich um den Typ SqlParameter. Sie können einen Parameter hinzufügen, indem Sie entweder die Add-Methode der Auflistung oder die Methode AddWithValue aufrufen.

Das Beispiel ParametrisierteAbfrage verwendet zum Hinzufügen die Methode AddWithValue. Die beiden Parameter werden mit statischen Werten gefüllt. In der Praxis würden Sie die Werte dem Eingabestrom oder beispielsweise einem Eingabefeld entnehmen.


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 24\ParametrisierteAbfrage
// ------------------------------------------------------------------
SqlConnection con = new SqlConnection("...");
string strSQL = "SELECT * FROM Products " +
                "WHERE ProductName = @Productname OR " +
                       "CategoryID = @CatID";
SqlCommand cmd = new SqlCommand(strSQL, con);
// Parameter hinzufügen und Werte übergeben
cmd.Parameters.AddWithValue("@Productname", "Konbu");
cmd.Parameters.AddWithValue("@CatID", "1");
con.Open();
SqlDataReader rd = cmd.ExecuteReader();
while (rd.Read())
  Console.WriteLine("{0,-5}{1,-35}{2}",
             rd["ProductID"], rd["ProductName"], rd["UnitPrice"]);
rd.Close();
con.Close();

Bei benannten Parametern ist die Reihenfolge der Parameter innerhalb der Parameters-Auflistung des SqlCommand-Objekts unbedeutend.

Bei beiden Parametern handelt es sich in diesem Beispiel um Zeichenfolgen, die auch als solche an die Datenbank weitergeleitet werden. Sie können hier jeden Datentyp angeben, denn das zweite Argument von AddWithValue ist vom Typ Object definiert.

Einen Haken kann der sehr einfache Einsatz der Methode AddWithValue dennoch haben. Verwenden Sie bei der Wertübergabe einen ungeeigneten Datentyp, dann behandelt die Datenbank die im Parameter gespeicherte Information vielleicht nicht so, wie Sie es erwarten. Unter Umständen gibt der SQL Server sogar eine Ausnahme vom Typ SqlException zurück, weil der übermittelte Parameter mit der Typdefinition der entsprechenden Spalte nicht übereinstimmt. Sie können das sehr leicht selbst testen, indem Sie im Code des Beispiels anstelle des Artikelbezeichners Konbu eine Integerzahl eintragen.

Der Datenbank diese Verantwortung zu übertragen, ist keine gute Lösung. Der richtige Datentyp sollte zumindest weitgehend im Code des Clients sichergestellt sein. Dazu bietet sich die vielfach überladene Methode Add an, die über den Parameterbezeichner hinaus auch den an die Datenbank übergebenen Datentyp steuert. Zudem gibt es noch die Möglichkeit, den Datentyp genauer zu spezifizieren. Beispielsweise können Zeichenfolgen eine unterschiedliche Länge aufweisen. Die Länge kann als drittes Übergabeargument bekannt gegeben werden. In unserem Beispiel oben könnten die beiden Anweisungen


cmd.Parameters.AddWithValue("@Name", "Konbu");
cmd.Parameters.AddWithValue("@CatID", 1);

durch


cmd.Parameters.Add("@Productname", SqlDbType.VarChar, 40).Value = 
"Konbu";
cmd.Parameters.Add("@CatID", SqlDbType.Int).Value = 1;

ersetzt werden.

Übergeben Sie einem der beiden Parameter einen Integer-Wert, wird keine Ausnahme ausgelöst. Das Ergebnis erscheint im ersten Moment ernüchternd und der vorher gemachten Aussage zu widersprechen, dass die Methode Add eine Typüberprüfung gewährleistet. Die Ursache ist allerdings einfach zu erklären: Die Integerzahl wird implizit als Zeichenfolge im Parameter eingetragen. Anders sieht es jedoch aus, wenn ein Parameter als Integer festgelegt wird und Sie versuchen, diesem eine Zeichenfolge zuzuweisen:


cmd.Parameters.Add("@Param", SqlDbType.Int).Value = "White";

Beim Aufruf von ExecuteReader wird die Ausnahme FormatException ausgelöst. Diese stammt nicht vom SQL Server, sondern wird von ADO.NET in der Client-Anwendung ausgelöst. Damit haben wir ein Ziel erreicht: Die Entlastung der Datenbank.


Hinweis

Der Datentyp, den Sie der Add-Methode übergeben, stammt aus der Enumeration SqlDbType. Die Mitglieder dieser Aufzählung beschreiben die Datentypen, die SQL Server standardmäßig bereitstellt.



Galileo Computing - Zum Seitenanfang

24.4.2 Die Klasse »SqlParameter« Zur nächsten ÜberschriftZur vorigen Überschrift

Solange nicht ausdrücklich Parameter hinzugefügt werden, ist die Parameters-Auflistung des SqlCommand-Objekts leer. Die Referenz auf die Auflistung erhalten Sie über die Eigenschaft Parameters. Ein Parameter wird durch den Aufruf der Methode Add oder AddWithValue hinzugefügt. Alle anderen Methoden der Auflistung gleichen denen aller anderen üblichen Auflistungen von .NET: Mit Count ruft man die Anzahl der Parameter ab, mit Remove wird ein Parameter gelöscht usw.

Die Methode Add ist vielfach überladen, AddWithValue überhaupt nicht. Beiden ist aber eines gemein: Der Rückgabewert ist die Referenz auf das hinzugefügte SqlParameter-Objekt. Meistens können Sie den Rückgabewert ignorieren. Er ist dann interessant, wenn man die Eigenschaften des Parameters auswerten oder vor dem Absetzen des SQL-Kommandos ändern möchte.

Zum Füllen des Parameters wird der Eigenschaft Value des SqlParameter-Objekts der entsprechende Wert zugewiesen:


cmd.Parameters["@ParameterName"].Value = "Chai";

Sie rufen den Indexer der SqlParameterCollection auf und übergeben den Bezeichner des Parameters. Alternativ können Sie auch den Index des entsprechenden Parameter-Objekts in der Auflistung verwenden.


Galileo Computing - Zum Seitenanfang

24.4.3 Asynchrone Abfragen Zur nächsten ÜberschriftZur vorigen Überschrift

Die Methoden ExecuteReader, ExecuteNonQuery oder ExecuteXmlReader arbeiten synchron. Das bedeutet, dass die Anwendung erst dann weiterarbeiten kann, wenn der SQL Server die Anfrage verarbeitet und die erste Datenzeile der Ergebnismenge zurückliefert. Dauert diese Operation eine längere Zeit, wirkt die Clientanwendung wie eingefroren.

ADO.NET löst dieses Problem durch die Bereitstellung asynchroner Methoden. Ergänzt werden die synchronen Methoden durch jeweils ein Methodenpaar: eine Methode hat das Präfix Begin, die zweite das Präfix End. Beispielsweise lauten die asynchronen Varianten von ExecuteReader BeginExecuteReader und EndExecuteReader, die wie folgt definiert sind:


public IAsyncResult BeginExecuteReader(IAsyncResult, Object)
public SqlDataReader EndExecuteReader(IAsyncResult)

Mit BeginExecuteReader wird die asynchrone Operation gestartet. Der aufrufende Code wartet jedoch nicht darauf, bis das Resultat vorliegt, sondern führt die Anweisungen aus, die dem asynchronen Aufruf folgen. Es stellt sich nur noch die Frage, wie das Clientprogramm darüber informiert wird, dass die asynchrone Operation beendet ist, und wie die Ergebnismenge abgefragt werden kann. Dazu bieten sich zwei Möglichkeiten an:

  • Sie fragen in einer Schleife permanent ab, ob die asynchrone Operation bereits beendet ist. Dieses Verfahren wird als Polling bezeichnet.
  • Sie definieren eine Rückrufmethode (Callback-Methode), die aufgerufen wird, sobald das Ergebnis vorliegt.

Beide Varianten werde ich Ihnen gleich an einem Beispiel vorstellen.

Asynchrone Operationen sind per Vorgabe nicht aktiviert. Damit das SqlConnection-Objekt auch asynchrone Abfragen ermöglicht, muss die Verbindungszeichenfolge um


Asynchronous Processing=true

ergänzt werden. Alternativ steht Ihnen auch die Eigenschaft AsynchronousProcessing des SqlConnectionStringBuilder zur Verfügung.

Damit wir in den folgenden beiden Beispielprogrammen auch eine Verzögerung der SQL Server-Anfrage simulieren können, schreiben wir eine Batch-Abfrage, der wir als erste Anweisung WAITFOR DELAY übergeben. Dieser Anweisung teilen wir mit, wie lange die Batch-Abfrage blockiert werden soll – beispielsweise zwei Sekunden:


WAITFOR DELAY '00:00:02'

Das Polling-Verfahren

Das erste asynchrone Beispielprogramm stellt das Polling-Verfahren vor. Sehen Sie sich zuerst den Beispielcode an.


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 24\ParametrisierteAbfrage
// ------------------------------------------------------------------
class Program {
  static void Main(string[] args) {
    SqlConnection con = new SqlConnection("...;
                        Asynchronous Processing=true");
    string strSQL = "WAITFOR DELAY '00:00:01';SELECT * FROM Products";
    SqlCommand cmd = new SqlCommand(strSQL, con);
    con.Open();
    // asynchroner Aufruf
    IAsyncResult result = cmd.BeginExecuteReader();
    int counter = 0;
    while (!result.IsCompleted) {
      DoSomething(counter);
      counter++;
    }
    Console.WriteLine("Das Ergebnis liegt vor: .....");
    SqlDataReader rd = cmd.EndExecuteReader(result);
    while(rd.Read())
      Console.WriteLine(rd["ProductName"]);
    Console.ReadLine();
  }
  static void DoSomething(int counter) {
    Console.WriteLine(counter);
  }
}

Beim Polling wird nicht darauf gewartet, bis das Vorliegen der Ergebnismenge der Clientanwendung signalisiert wird. Stattdessen wird in einer Schleife abgefragt, ob der Datenbankserver die Anfrage fertig bearbeitet hat. Der Aufruf der Methode BeginExecuteReader liefert ein Objekt zurück, das die Schnittstelle IAsyncResult implementiert. Dieses Objekt beschreibt den Status der asynchronen Abfrage, unter anderem durch die Eigenschaft IsCompleted. Der Wert lautet false, falls die Anfrage noch nicht beendet ist, ansonsten true. Beachten Sie, dass in diesem Beispiel die parameterlose Methode BeginExecuteReader eingesetzt wird.

Wir werten im Beispielprogramm IsCompleted aus. Solange die Ergebnismenge noch nicht vorliegt, wird von der Clientanwendung eine andere Aufgabe erledigt. Hierbei handelt es sich um den Aufruf der Methode DoSomething, die einen einfachen Zählerstand in das Konsolenfenster schreibt. Ist die Anfrage an den Datenbankserver beendet, kann das Ergebnis geholt werden. Dazu dient die Methode EndExecuteReader, die ihrerseits die Referenz auf ein SqlDataReader-Objekt bereitstellt, das wir zur Ausgabe der Spalte ProductName benutzen.

Rückrufmethode bereitstellen

Während beim Polling fortwährend geprüft wird, ob der Datenbankserver die Anfrage bearbeitet hat, wird durch das Bereitstellen einer Rückrufmethode auf das Signal der Datenbank gewartet, dass die Operation beendet ist. Das Signal ist der Aufruf einer Methode im Client, der sogenannten Rückrufmethode.

Die »Adresse« der Rückrufmethode, die im folgenden Beispiel CallbackMethodSample heißt, wird dem ersten Parameter der überladenen Methode BeginExecuteReader übergeben. Es handelt sich dabei um einen Parameter vom Typ des Delegate AsyncCallback, der eine Rückrufmethode vorschreibt, die einen Parameter vom Typ IAsyncResult hat und die ihrerseits selbst void ist. BeginExecuteReader definiert mindestens noch einen zweiten Parameter. Dieser ist vom Typ Object und akzeptiert somit jedes beliebige Objekt. Das Objekt wird der Eigenschaft AsyncState der IAsyncResult-Schnittstelle zugewiesen und kann in der Callback-Methode abgerufen werden. Im Beispiel wird die Referenz auf das SqlCommand-Objekt übergeben.

Nach Beendigung der asynchronen Operation wird die Rückrufmethode ausgeführt, aus der heraus EndExecuteReader aufgerufen wird. Das dazu notwendige SqlCommand-Objekt wurde dem zweiten Parameter der Methode BeginExecuteReader übergeben und kann nach Auswertung der Eigenschaft AsyncState des IAsyncResult-Parameters und vorheriger Konvertierung benutzt werden. Danach steht auch der DataReader zur Verfügung.

Nun aber das vollständige Beispiel:


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 24\CallbackMethodSample
// ------------------------------------------------------------------
class Program {
  static void Main(string[] args) {
    SqlConnection con = new SqlConnection("...;
                        Asynchronous Processing=true");
    string strSQL = "SELECT * FROM Products";
    SqlCommand cmd = new SqlCommand(strSQL, con);
    con.Open();
    // Delegate initialisieren
    AsyncCallback callback = new AsyncCallback(CallbackMethod); 
    // asynchrone Operation starten
    cmd.BeginExecuteReader(callback, cmd); 
    // Simulation eines asynchronen Szenarios
    for (int counter = 0; counter < 100; counter++)
    {
        Console.WriteLine(new string('-', 50));
        Console.WriteLine("Counter = {0}", counter);
        Console.WriteLine(new string('-', 50));
    }
    Console.ReadLine();
  }
  static void CallbackMethod(IAsyncResult result) {
    SqlCommand cmd = (SqlCommand)result.AsyncState; 
    // Ergebnisliste holen
    SqlDataReader rd = cmd.EndExecuteReader(result);
    while(rd.Read())
      Console.WriteLine(rd["ProductName"]);
    rd.Close();
  }
}

Diese Konsolenausgabe des Beispielcodes ist in Abbildung 24.2 zu sehen. Es ist sehr schön zu erkennen, wie beide Operationen parallel ablaufen.

Abbildung 24.2 Die Ausgabe des Beispielprogramms »CallbackMethodSample«


Galileo Computing - Zum Seitenanfang

24.4.4 Gespeicherte Prozeduren (Stored Procedures) topZur vorigen Überschrift

Bei einer gespeicherten Prozedur (Stored Procedure) handelt es sich um eine Gruppe von SQL-Anweisungen, die kompiliert werden. Das hat einen entscheidenden Vorteil: Die Leistung verbessert sich deutlich, wenn die gespeicherte Prozedur ausgeführt wird, da die SQL-Anweisungen nicht bei jedem Aufruf neu kompiliert werden müssen.

Eine gespeicherte Prozedur ist nicht schwierig zu verstehen. Wir wollen uns das an einem Beispiel ansehen.


CREATE PROCEDURE SearchProducts
(
   @Price money,
   @OrderedUnits smallint
)
AS
   SELECT *
   FROM Products
   WHERE UnitPrice < @Price AND UnitsOnOrder = @OrderedUnits

Diese gespeicherte Prozedur beschreibt eine Auswahlabfrage, die alle Artikel der Tabelle Products liefert, die eine bestimmte Preisgrenze unterschreiten und eine bestimmte Anzahl von Bestelleinheiten haben. Damit ist die Stored Procedure gleichbedeutend mit folgendem SqlClient-Datenprovider-Befehl:


SELECT *
FROM Products
WHERE UnitPrice < @Price AND UnitsOnOrder = @OrderedUnits

Gespeicherte Prozeduren bieten sich besonders dann an, wenn ein Kommando sehr häufig ausgeführt werden soll. Sie sind nicht nur leistungsfähiger als normale SQL-Kommandos, sondern bieten auch darüber hinaus weitergehende Möglichkeiten: Stored Procedures können Berechnungen ausführen, Ein- und Ausgabeparameter entgegennehmen (ähnlich Wert- und Referenzparametern) oder ein Resultat an den Aufrufer liefern.

Mehr möchte ich Ihnen an dieser Stelle nicht zu den gespeicherten Prozeduren sagen. Es gibt viel Literatur zu diesem Thema, wenn Sie es vertiefen möchten. Ich werde Ihnen nachher an einem komplexeren Beispiel zeigen, wie gespeicherte Prozeduren, die die aufgeführten Features haben, mit ADO.NET-Code behandelt werden.

Gespeicherte Prozeduren in Visual Studio 2010 erstellen

Ein herkömmlicher SQL-Befehl wird vom Client gegen die Datenbank abgesetzt. Gespeicherte Prozeduren sind, soweit die Datenbank diese unterstützt, Elemente der Datenbank selbst, so wie beispielsweise die Tabellen oder Sichten. Wenn Sie wollen, können Sie sehr einfach aus Visual Studio 2010 heraus gespeicherte Prozeduren zu einer Datenbank hinzufügen. Öffnen Sie dazu den Server-Explorer in Visual Studio 2010. In diesem finden Sie den Knoten Datenverbindungen. Im Kontextmenü dieses Knotens wählen Sie Verbindung hinzufügen. Es öffnet sich ein Dialog, wie er in Abbildung 24.3 zu sehen ist. Tragen Sie im oberen Kombinationslistenfeld den Namen des Servers ein, auf dem die SQL Server-Datenbank installiert ist, zu der Sie Verbindung aufnehmen wollen. Per Vorgabe ist der Dialog bereits so voreingestellt, dass davon ausgegangen wird, es handele sich um SQL Server. Sie können die Verbindung natürlich auch zu einer anderen Datenbank aufbauen, müssen dann aber zuvor die eingetragene Datenquelle entsprechend anpassen.

Abbildung 24.3 Dialog zum Hinzufügen einer Datenbankverbindung

Haben Sie die Installationsvorgaben des SQL Servers unverändert übernommen, so ist die Windows-Authentifizierung eingestellt, und Sie brauchen, soweit Sie mit entsprechenden administrativen Rechten ausgestattet sind, keine Änderungen an den Anmeldeinformationen vornehmen. Anschließend wählen Sie die gewünschte Datenbank aus. Sie können die eingestellten Verbindungsdaten nun testen.

Im Server-Explorer wird die neue Verbindung zur Datenbank eingetragen. Unter den datenbankspezifischen Knoten finden Sie nun auch Gespeicherte Prozeduren. Klicken Sie dann im Kontextmenü des Knotens auf Neue gespeicherte Prozedur hinzufügen (siehe Abbildung 24.4).

Abbildung 24.4 Gespeicherte Prozedur zur Datenbank hinzufügen

Im Codeeditor wird daraufhin ein weiteres Fenster geöffnet, in dem bereits die elementare Struktur der Stored Procedure vorgegeben ist (siehe Abbildung 24.5). Eine gespeicherte Prozedur wird mit CREATE PROCEDURE eingeleitet; dem schließt sich der Bezeichner an. Einige Teile der Struktur sind mit /*...*/ auskommentiert. Dazu gehört auch der Block, in dem die Parameter angegeben werden. Hinter AS folgen die SQL-Anweisungen. Eine gespeicherte Prozedur wird mit dem optionalen RETURN abgeschlossen, das den Rückgabewert der gespeicherten Prozedur angibt.

Als Erstes sollten Sie der Stored Procedure einen beschreibenden Namen geben, z. B. SearchProducts. Parameter werden im Block zwischen CREATE PROCDURE und AS definiert. Dabei wird zuerst der Parametername angegeben, der das Präfix @ haben muss. Dahinter folgt der Datentyp. Mehrere Parameter in einer Stored Procedure werden durch ein Komma getrennt.

Standardmäßig sind alle Parameter Eingabeparameter, die von der Stored Procedure zur Ausführung benötigt werden, selbst aber kein Resultat zurückliefern. Gespeicherte Prozeduren kennen aber auch Ausgabeparameter, die mit Referenzparametern vergleichbar sind. Diese liefern dem Aufrufer ein Ergebnis. Ausgabeparameter werden mit OUTPUT gekennzeichnet.

Abbildung 24.5 Das Fenster einer neuen gespeicherten Prozedur im Codeeditor

Nachdem der SQL-Code im Codeeditor eingetragen ist, können Sie die Stored Procedure speichern. Gespeichert wird die Stored Procedure allerdings nicht im Projekt, sondern in der Datenbank, was Sie auch sofort im Server-Explorer erkennen. Beim Speichervorgang wird die Syntax überprüft. Sollte die SQL-Syntax einen Fehler aufweisen, werden Sie mit einer Fehlermeldung darauf aufmerksam gemacht.

Damit hört die Unterstützung von Visual Studio 2010 aber nicht auf. Sie können Ihre neue gespeicherte Prozedur auch in der Entwicklungsumgebung testen. Dazu sollte sich der Mauszeiger über dem Code-Fenster der Stored Procedure befinden. Öffnen Sie dann das Kontextmenü, und wählen Sie Ausführen. Es öffnet sich ein Dialog, in dem Sie in der Spalte Wert den Parametern die gewünschten Daten zuweisen (siehe Abbildung 24.6).

Abbildung 24.6 In diesem Dialog weisen Sie den Parametern Werte zu.

Wenn Sie anschließend auf OK klicken, sehen Sie im Fenster Ausgabe das Ergebnis des Aufrufs (siehe Abbildung 24.7).

Abbildung 24.7 Ergebnis des Aufrufs der Stored Procedure »SearchProducts«

Gespeicherte Prozedur aufrufen

Die soeben entwickelte gespeicherte Prozedur SearchProducts soll nun aufgerufen werden. Prinzipiell ist der Weg ähnlich dem, den wir beim Aufruf einer parametrisierten Abfrage beschritten haben. Es gibt aber einen ganz wichtigen Unterschied: Wir müssen dem SqlCommand-Objekt ausdrücklich mitteilen, dass es kein SQL-Kommando, sondern eine gespeicherte Prozedur ausführen soll. Um dem Objekt den Typ eines Kommandos mitzuteilen, wird der Eigenschaft CommandType die passende Information übergeben.

Die Eigenschaft ist vom Typ der gleichnamigen Enumeration, die angibt, wie das unter der Eigenschaft CommandText angegebene Kommando zu interpretieren ist.


Tabelle 24.1 Mitglieder der Enumeration »CommandType«

Member Beschreibung
StoredProcedure

CommandText enthält den Namen einer gespeicherten Prozedur.

TableDirect

CommandText enthält den Namen einer Tabelle.

Text

(Standard) CommandText enthält ein SQL-Kommando.



Hinweis

Lautet die Einstellung CommandType.TableDirect, können Sie der Eigenschaft einen Tabellennamen zuweisen. Das ist gleichwertig mit dem SQL-Befehl SELECT * FROM <Tabellenname>.


Bisher haben wir die Eigenschaft CommandType nicht benutzt, weil wir immer ein SQL-Kommando abgesetzt haben, das durch die Standardeinstellung Text beschrieben wird. Da wir nun eine Stored Procedure ausführen wollen, müssen wir CommandType den Wert CommandType.StoredProcedure zuweisen. Das SqlCommand-Objekt benutzt diese Information, um die Syntax für den Aufruf der gespeicherten Prozedur zu generieren.

Komplexe gespeicherte Prozeduren

Eine gespeicherte Prozedur ist nicht immer so einfach aufgebaut wie SearchProducts, die nur Datensätze als Ergebnis der Ausführung zurückliefert. Eine gespeicherte Prozedur kann sowohl über die Parameterliste als auch über RETURN Werte an den Aufrufer zurückliefern. Dazu ein Beispiel:


CREATE PROCEDURE GetProduct
(
  @id int,
  @Artikel varchar(40) OUTPUT,
  @Preis money OUTPUT
)
AS
  SELECT @Artikel=ProductName, @Preis=UnitPrice
  FROM Products
  WHERE ProductID=@id
RETURN @@ROWCOUNT

Die Stored Procedure definiert neben dem Eingabeparameter @id mit @zuname und @vorname auch zwei Ausgabeparameter, denen beim Aufruf zwar kein Wert übergeben wird, die aber einen Wert zurückliefern. Der Rückgabewert @@ROWCOUNT ist eine Systemfunktion von SQL Server, die die Anzahl der Zeilen angibt, auf die sich die letzte Anweisung ausgewirkt hat.


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 24\KomplexeStoredProcedure
// ------------------------------------------------------------------
class Program {
  static void Main(string[] args) {
    SqlConnection con = new SqlConnection("...");
    // SqlCommand-Objekt definieren
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = con;
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "GetProduct";
    // SqlParameter definieren
    cmd.Parameters.Add("@RetValue", SqlDbType.Int);
    cmd.Parameters.Add("@id", SqlDbType.Int);
    cmd.Parameters.Add("@Artikel", SqlDbType.VarChar, 40);
    cmd.Parameters.Add("@Preis", SqlDbType.Money);
    cmd.Parameters["@RetValue"].Direction = ParameterDirection.ReturnValue;
    cmd.Parameters["@Artikel"].Direction = ParameterDirection.Output;
    cmd.Parameters["@Preis"].Direction = ParameterDirection.Output;
    // Übergabewert angeben
    cmd.Parameters["@id"].Value = 1;
    con.Open();
    cmd.ExecuteNonQuery();
    // SqlParameterCollection auswerten
    if ((int)(cmd.Parameters["@RetValue"].Value) == 1) {
      Console.WriteLine("Zuname: {0}", cmd.Parameters["@Artikel"].Value);
      Console.WriteLine("Vorname:{0}", cmd.Parameters["@Preis"].Value);
    }
    Console.WriteLine("{0} Datensatz gefunden.",
                        cmd.Parameters["@RetValue"].Value);
    con.Close();
    Console.ReadLine();
  }
}

Im ersten Schritt wird nach dem Öffnen der Verbindung das SqlCommand-Objekt definiert. Anschließend wird für jeden Parameter der gespeicherten Prozedur der Parameter-Auflistung ein SqlParameter-Objekt hinzugefügt. Als Parameter wird auch der von RETURN gelieferte Rückgabewert verstanden. Damit benötigt der Aufruf insgesamt vier Parameter-Objekte.

SqlParameter können unterschiedliches Verhalten haben. Dies muss ADO.NET wissen, um die gespeicherte Prozedur richtig zu verarbeiten. Standardmäßig beschreibt ein SqlParameter-Objekt einen Eingabeparameter. Abweichungen davon müssen über die Direction-Eigenschaft des Parameter-Objekts festgelegt werden, die vom Typ ParameterDirection ist, einer Enumeration mit vier Konstanten.


Tabelle 24.2 Mitglieder der Enumeration »ParameterDirection«

Member Beschreibung
Input

Der Parameter ist ein Eingabeparameter.

InputOutput

Der Parameter unterstützt sowohl die Eingabe als auch die Ausgabe.

Output

Der Parameter ist ein Ausgabeparameter.

ReturnValue

Der Parameter stellt einen Rückgabewert dar.


Jetzt muss die Parameterliste gefüllt werden, um das SqlCommand-Objekt anschließend auszuführen. Dazu wird dem Parameter @id die Spalte ProductID zugewiesen, anhand derer der gesuchte Artikel identifiziert werden soll. Weil die gespeicherte Prozedur keine Datensatzliste zurückgibt, genügt der Aufruf der Methode ExecuteNonQuery auf dem SqlCommand-Objekt. Das Ergebnis des Aufrufs kann danach ausgewertet werden, indem sowohl der Inhalt des Rückgabewertes als auch der Inhalt der Ausgabeparameter abgerufen werden.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2010

Visual C# 2010
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2010
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern