Lies in den Artikel rein und unten bekommst Du ein unschlagbares Angebot!
Wenn Sie von Access kommen und es gewohnt sind, Abfragen mit der Entwurfsansicht über die Benutzeroberfläche anzulegen, kann schon das Zusammenstellen von SQL-Abfragen nervig sein. Aber diese kann man unter Access immerhin noch aus der SQL-Ansicht einer Abfrage ermitteln. Was aber, wenn man nun Abfragen in LINQ formulieren soll, wo es noch nicht einmal eine grafische Entwurfsansicht gibt Nun: Es gibt auch unter Entity Framework die Möglichkeit, SQL-Befehle abzusetzen. Das ist auch deshalb interessant, weil Sie so auch gespeicherte Prozeduren ausführen können. Wie das gelingt, zeigt der vorliegende Artikel.
Wenn Sie in Entity Framework direkt mit einer SQL-Abfrage auf die Daten der zugrunde liegenden Tabellen zugreifen wollen, können Sie drei verschiedene Methoden nutzen:
- Jede DbSet-Auflistung bietet die Methode SqlQuery an, zum Beispiel: Kunden.SqlQuery
- Die Methode SqlQuery finden Sie auch für das Objekt Database der Kontextklasse, hier meist dbContext genannt: dbContext.Database.SqlQuery
- Die mit dbContext referenzierte Kontextklasse bietet über das Objekt Database außerdem noch die Methode ExecuteSqlCommand an: dbContext.Database.ExecuteSqlCommand
Die Methode SqlQuery der DbSet-Klasse
Wenn Sie die Methode SqlQuery einer der DbSet-Klassen des Entity Data Models aufrufen, muss die Abfrage als Ergebnis Entitäten des Typs der jeweiligen DbSet-Klasse liefern. Wenn Sie also etwa auf Daten der Tabelle Kunden zugreifen wollen, würde die folgende Methode funktionieren, die wir über eine Schaltfläche des Fensters MainWindow des Beispielprojekts aufrufen:
Private Sub btnKundenAbfragenDbSet_Click(sender As Object, e As RoutedEventArgs) Dim dbContext As BestellverwaltungContext Dim Kunden As List(Of Kunde) Dim Kunde As Kunde dbContext = New BestellverwaltungContext Kunden = dbContext.Kunden.SqlQuery("SELECT * FROM Kunden").ToList() For Each Kunde In Kunden Debug.Print(Kunde.ID.ToString() + " " + Kunde.Nachname) Next End Sub
Hie definieren wir eine List-Variable Kunden für Elemente des Typs Kunde sowie eine Kunde-Variable gleichen Namens. Diese füllen wir dann mit dem Ergebnis der SqlQuery-Methode mit dem Parameter SELECT * FROM Kunden, das wir dann mit ToList() noch in eine Liste konvertieren. Anschließend durchlaufen wir die Elemente in einer For Each-Schleife und geben die Werte der Felder ID und Nachname im Ausgabebereich aus.
Abfragen mit INNER JOIN
Sie können auch weitere Tabellen zur Abfrage hinzufügen. Im folgenden Beispiel haben wir etwa die Anweisung mit der SqlQuery-Methode wie folgt geändert und die Tabelle Anreden per INNER JOIN hinzugefügt, um nach allen Kunden zu filtern, deren Anrede Frau lautet:
Kunden = dbContext.Kunden.SqlQuery("SELECT Kunden.* FROM Kunden INNER JOIN Anreden ON Kunden.AnredeID = Anreden.ID WHERE Anreden.Name = ''Frau''").ToList() ''Test
Das Ergebnis sehen Sie in Bild 1.
Bild 1: Ausgabe des Ergebnisses der SqlQuery-Methode
Wichtig ist nur, dass das Abfrageergebnis nur Elemente der Tabelle Kunden enthält. Wir probieren einmal die folgende SQL-Abfrage in der gleichen Methode aus, welche noch das Feld Anrede der Tabelle Anreden mit dem Abfrageergebnis zurückliefert:
SELECT Kunden.*, Anreden.Name FROM Kunden INNER JOIN Anreden ON Kunden.AnredeID = Anreden.ID WHERE Anreden.Name = ''Frau''
Dies liefert das gleiche Ergebnis und keinen Fehler, aber der Inhalt des Feldes Name der Tabelle Anreden ist dennoch nicht verfügbar, da wir ja über die Auflistung Kunden nur auf Elemente der Klasse Kunde zugreifen können. Aber sollte man hier nicht mit einem Fehler rechnen, da die SELECT-Abfrage auch Felder liefert, die so nicht in der Zielklasse Kunde vorkommen Wir probieren es einmal aus und verwenden die folgende Abfrage, die neben den Feldern der Tabelle Kunde auch ein Feld der Tabelle Produkte enthält:
Kunden = dbContext.Kunden.SqlQuery("SELECT Kunden.*, Produkte.Name FROM (Kunden INNER JOIN Bestellungen ON Kunden.ID = Bestellungen.KundeID) INNER JOIN (Produkte INNER JOIN Bestellpositionen ON Produkte.ID = Bestellpositionen.ProduktID) ON Bestellungen.ID = Bestellpositionen.BestellungID").ToList()
Führen wir die Methode aus und schauen uns den Aufbau des Objekts Kunde wie in Bild 2 an, enthält dieses genau die in der Klasse definierten Elemente. Nun probieren wir auch noch den gegenteiligen Fall aus, indem wir nicht alle Elemente der Klasse Kunde mit Werten aus der zugrunde liegenden Tabelle Kunden füllen, sondern nur mit dem Feld Kunden.ID:
Bild 2: Die Kunde-Klasse enthält nur die definierten Elemente.
Kunden = dbContext.Kunden.SqlQuery("SELECT Kunden.ID, Produkte.Name FROM (Kunden INNER JOIN Bestellungen ON Kunden.ID = Bestellungen.KundeID) INNER JOIN (Produkte INNER JOIN Bestellpositionen ON Produkte.ID = Bestellpositionen.ProduktID) ON Bestellungen.ID = Bestellpositionen.BestellungID").ToList()
Dies sorgt dann allerdings für eine Fehlermeldung mit dem folgenden Text:
Der Datenleser ist mit dem angegebenen Wert für ''EDMSQL.Kunde'' nicht kompatibel. Ein Element vom Typ (''Firma'') weist keine entsprechende Spalte im gleichnamigen Datenleser auf.
Die Abfrage muss also zwingend alle Eigenschaften der Zielklasse füllen, darf aber auch weitere Felder ausgeben – die dann allerdings nicht berücksichtigt werden.
Daten aus gespeicherten Prozeduren laden
Auf die gleiche Weise können Sie auch Daten aus gespeicherten Prozeduren laden. Dazu fügen wir der SQL Server-Datenbank, die in der Verbindungszeichenfolge genannt ist, eine gespeicherte Prozedur an. Das erledigen wir, in dem wir in Visual Studio den Bereich SQL Server-Objekt-Explorer einblenden (Menüpunkt Ansicht|SQL Server-Objekt-Explorer). Hier wählen Sie die Datenbank zu unserem Projekt aus und navigieren zum Eintrag Gespeicherte Prozeduren. Das Kontextmenü dieses Eintrags bietet den Befehl Neue gespeicherte Prozedur hinzufügen… an, den Sie nun aufrufen (siehe Bild 3).