38.3 Der EntityClient-Provider
Neben LINQ to Entities und Entity SQL gibt es noch eine dritte Möglichkeit, das Entity Data Model abzufragen: mit dem EntityClient-Provider, der alle erforderlichen Klassen und Schnittstellen im Namespace System.Data.EntityClient zur Verfügung stellt.
Es gibt zwischen dem EntityClient-Provider und LINQ to Entities einen großen Unterschied: Mit EntityClient werden die Abfrageresultate nicht als Objekte materialisiert. Stattdessen werden die Daten in Form von Zeilen und Spalten als Objekte vom Typ EntityDataReader an den Aufrufer zurückgeliefert. Dieses Objekt ist dem DataReader von ADO.NET sehr ähnlich. Das bedeutet auch, dass die Daten nur gelesen, nicht aber verändert werden können.
Sollten Sie mit dem SqlClient-, OleDb- oder einem anderen ADO.NET-Provider Erfahrung gesammelt haben, werden Sie sehr schnell feststellen, dass der EntityClient-Provider diesen sehr ähnlich ist. Das macht sich beispielsweise auch in den Klassenbezeichnern bemerkbar. Statt SqlConnection heißt die entsprechende Klasse des EntityClient-Providers EntityConnection, statt SqlCommand gibt es die Klasse EntityCommand.
Da EntityClient nur Daten bereitstellt, die nicht verändert werden können, werden Sie Klassen analog zum DataSet oder zum SqlDataAdapter vergeblich suchen. Im Wesentlichen beschränkt sich der EntityClient-Provider auf die folgenden Klassen:
- EntityConnection
- EntityCommand
- EntityParameter
- EntityDataReader
- EntityTransaction
Die Eigenschaften und Methoden sind denen der entsprechenden Klassen der ADO.NET-Provider sehr ähnlich. Natürlich gibt es kleine Unterschiede, insbesondere hinsichtlich der Abfragezeichenfolge, die nicht in SQL, sondern in Entity SQL formuliert und folglich gegen den EntityClient-Provider abgesetzt wird. Dabei sollten Sie nicht vergessen, dass sich der EntityClient-Provider nicht direkt mit der Datenbank verbindet, sondern immer von dem ADO.NET-Datenprovider abhängt (siehe Abbildung 37.13).
Sehen wir uns nun ein Beispielprogramm an.
EntityConnection con = new EntityConnection("Name=NorthwindEntities");
con.Open();
string query = "SELECT VALUE p FROM NorthwindEntities.Products As p";
EntityCommand cmd = new EntityCommand();
cmd.CommandText = query;
cmd.Connection = con;
EntityDataReader reader =
cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);
while (reader.Read())
{
Console.WriteLine("{0,-4}{1}", reader["ProductID"], reader["ProductName"]);
}
reader.Close();
con.Close();
Listing 38.25 Beispiel mit EntityClient-Provider
Sollten Sie sich mit den ADO.NET-Datenprovidern beschäftigt haben, werden Sie die Ähnlichkeit sofort erkennen. Das Beispiel selbst ist sehr einfach gestrickt: Es wird zuerst eine Datenverbindung aufgebaut, danach ein Kommando definiert und die Abfrage ausgeführt. Die Resultate werden in die Konsole geschrieben.
38.3.1 Verbindungen mit »EntityConnection«
Ein Objekt vom Typ EntityConnection ist dafür verantwortlich, die Verbindung zum darunter liegenden ADO.NET-Provider aufzubauen. Der Konstruktor ist selbstredend überladen. In Listing 38.25 wurde die Variante gewählt, die eine formatierte Zeichenfolge entgegennimmt. Diese beginnt mit Name=. Dahinter wird der Bezeichner der Verbindungszeichenfolge aus der Konfigurationsdatei angegeben, also:
Name=NorthwindEntities
Im Zusammenhang mit dem Typ EntityConnection ist die Eigenschaft StoreConnection erwähnenswert. Diese Eigenschaft filtert aus der für den Objektkontext hinterlegten Verbindungszeichenfolge denjenigen Teil heraus, der die Verbindung zur physikalischen Datenbank beschreibt. Die Eigenschaft StoreConnection ist vom Typ DbConnection. Es wird Ihnen damit ermöglicht, direkt ein SQL-Statement zur Datenbank zu schicken. Beachten Sie bitte, dass ich »SQL-Statement« geschrieben habe und nicht »Entity SQL«! Denn Letzteres wird zu einer Ausnahme führen. Listing 38.26 zeigt, wie Sie die Eigenschaft StoreConnection im Programmcode benutzen können.
using(NorthwindEntities context = new NorthwindEntities())
{
DbConnection con =
(context.Connection as EntityConnection).StoreConnection;
con.Open();
Console.WriteLine("Verbindungszeichenfolge:");
Console.WriteLine(con.ConnectionString + "\n");
DbCommand cmd = con.CreateCommand();
cmd.CommandText = "SELECT * FROM Products";
DbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader[1]);
}
}
Listing 38.26 Die Eigenschaft »StoreConnection«
Es bleibt noch die Frage zu klären, in welchen Situationen sich der Direktzugriff auf die Datenbank anbietet, der mit StoreConnection eingeleitet wird. Dazu muss man sich darüber im Klaren sein, dass das Entity Framework nicht alle denkbaren Szenarien unterstützt. Um Ihnen ein Beispiel zu nennen: Derzeit werden gespeicherte Prozeduren nicht unterstützt, wenn sie mehrere Ergebnislisten liefern. Allerdings muss in diesem Zusammenhang auch erwähnt werden, dass es noch andere Möglichkeiten gibt, SQL-basierte Abfragen gegen eine Datenbank abzusetzen.
38.3.2 Die Klasse »EntityCommand«
Die Klasse EntityCommand dient dazu, Entity-SQL-Abfragen oder gespeicherte Prozeduren auszuführen, und unterscheidet sich nicht von den Command-Klassen der anderen ADO.NET-Datenprovider: Der Eigenschaft Connection wird das Connection-Objekt übergeben und der CommandText-Eigenschaft eine Zeichenfolge, die die Abfrage beschreibt. Beim EntityClient-Provider handelt es sich um ein Entity-SQL-Statement.
Ähnlich wie bei den ADO.NET-Providern wird die Abfrage mit den Methoden ExecuteReader, ExecuteNonQuery oder ExcecuteScalar abgesetzt, vorausgesetzt, die durch das EntityConnection-Objekt beschriebene Verbindung ist geöffnet.
Die ExecuteReader-Methode weist eine Besonderheit auf: Sie müssen einen Parameter vom Typ CommandBehavior.SequentialAccess übergeben. Das hat Konsequenzen, wenn Sie die Spalten der Rückgabe auswerten, denn Sie müssen die Spalten in der Reihenfolge abgreifen, in der sie eintreffen. Wir können uns das sehr einfach verdeutlichen, wenn wir uns noch einmal den Ausgabecode des Listings 38.25 ansehen:
Console.WriteLine("{0,-4}{1}", reader["ProductID"], reader["ProductName"]);
Da die Spalte ProductID mit Index=0 vor ProductName mit Index=1 steht, haben wir keine Probleme. Vertauschen wir jedoch die Positionen, also
Console.WriteLine("{0}{1}", reader["ProductName"], reader["ProductID"]);
wird eine InvalidOperationException ausgelöst. Genau in diesem Punkt unterscheidet sich ein EntityCommand-Objekt von den Command-Objekten der ADO.NET-Provider, bei denen der Zugriff auf die Spalten innerhalb des Readers beliebig ist. Allerdings müssen wir nicht alle Spalten der Reihe nach auswerten, sondern können uns durchaus nur für diejenigen entscheiden, an denen wir interessiert sind. Hauptsache, die sequenzielle Reihenfolge wird eingehalten.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.