Datenpunkte
Übersicht über ADO.NET Entity Framework
John Papa
Codedownload verfügbar unter:
DataPoints2007_07.exe
(1590 KB)
Browse the Code Online

Inhalt
In der nächsten Version von Visual Studio® (Codename „Orcas“) enthält ADO.NET das neue Feature Entity Framework. Hierbei werden Daten mithilfe eines Objektmodells anstelle eines logischen/relationalen Datenmodells behandelt. Mit Entity Framework kann das logische Datenschema in ein konzeptionelles Modell abstrahiert werden. Über die Objektdienste und den neuen Datenanbieter EntityClient bestehen verschiedene Interaktionsmöglichkeiten mit dem konzeptionellen Modell. Im Artikel dieses Monats wird erläutert, was Entity Framework ist, wie es in eine Anwendung passt, und wie es entworfen und programmiert werden kann.
Entity Framework abstrahiert die logische Datenbankstruktur mithilfe einer konzeptionellen Ebene, einer Zuordnungsebene und einer logischen Ebene. In diesem Artikel werden Ziel und Zweck der drei Ebenen erörtert. Außerdem werden Sie in EntityClient und die neue Sprache Entity SQL eingeführt, die mit den konzeptionellen Ebenen des Entity Data Model (EDM) interagieren können.
Eine Alternative zu EntityClient sind die Objektdienste. Mit den Objektdiensten in Entity Framework müssen Entwickler nicht mehr so viel Datenzugriffscode schreiben. Weiterhin wird aufgezeigt, wie die Objektdienste mit Entity SQL und LINQ to Entities zur Interaktion mit EDM verwendet werden und wie konzeptionelle Entitäten von EDM abgerufen werden können.
Ich möchte darauf hinweisen, dass alle Beispiele dieses Artikels mit ADO.NET und Entity Framework funktionieren, die in der Beta 1-Version von „Orcas“ enthalten sind.
Entity Framework-Komponenten
Mit Entity Framework reduziert sich der Wartungsaufwand, die Datenstruktur wird in einer geschäftsfreundlicheren (und weniger normalisierten) Art und Weise abstrahiert, die Dauerhaftigkeit der Daten ist eher gegeben, und Entwickler müssen weniger Datenzugriffscode schreiben. Bei Verwendung mit LINQ to Entities (wird später angesprochen) können außerdem Kompilierungsfehler reduziert werden, da aus dem konzeptionellen Modell Klassen generiert werden, die streng nach Typen geordnet sind.
Entwickler können Code anhand des von Entity Framework generierten konzeptionellen Modells schreiben. Über den neuen Datenanbieter EntityClient und die neue Sprache Entity SQL (ähnlich T-SQL) findet eine direkte Interaktion mit diesem Modell statt. Das EntityClient-Modell ähnelt den bekannten ADO.NET-Objekten und verwendet EntityConnection- und EntityCommand-Objekte zur Rückgabe eines DbDataReader. Als weitere Option können Entwickler die Objektdienste entweder mit einem ObjectQuery-Objekt mit Entity SQL oder LINQ to Entities verwenden. Mithilfe der Objektdienste können Entwickler die vom konzeptionellen Modell generierten Klassen nutzen, die Features wie streng typisierte Objekte und Dauerhaftigkeit bieten (siehe Abbildung 1).
Abbildung 1 Überblick über Entity Framework
Über diese Datenzugriffsverfahren können Entwickler mit den konzeptionellen Entitäten von EDM interagieren. Die Schichten des EDM existieren als XML-Dateien. Derzeit kann EDM über ein Befehlszeilentool (EDMGEN. EXE), manuell oder über einen Assistenten in Visual Studio generiert werden.
Entitätsdatenmodell
Die Modelle bilden den Kern von Entity Framework. Entity Framework unterstützt ein logisches Speichermodell, das das relationale Schema einer Datenbank darstellt. Eine relationale Datenbank speichert Daten oft in anderer Weise als sie von einer Anwendung verwendet werden. Deshalb müssen Entwickler die Daten gezwungenermaßen in der gleichen Struktur wie in der Datenbank abrufen. Entwickler lesen die Daten dann oft in Geschäftsentitäten ein, die sich besser zum Verarbeiten von Geschäftsregeln eignen. In diesem Beispiel wird das Schema der relationalen Datenbank in einem logischen Modell dargestellt. Die Geschäftsentitäten stehen für das konzeptionelle Modell. Entity Framework schließt diese Lücke in den Modellen mithilfe einer Zuordnungsschicht. Folglich gibt es im Entity Framework-Modell drei aktive Schichten:
- Konzeptionelle Schicht
- Zuordnungsschicht
- Logische Schicht
Diese drei Schichten ermöglichen die Zuordnung von Daten einer relationalen Datenbank zu einem stärker objektorientierten Geschäftsmodell. Entity Framework bietet die Möglichkeit, diese Schichten mithilfe von XML-Dateien zu definieren. Außerdem wird basierend auf dem Schema des konzeptionellen Modells eine Reihe von Klassen generiert. Anhand dieser Klassen können Sie direkt die Interaktion mit den Daten programmieren. Dadurch erhalten Sie eine Abstraktionsebene und können die Programmierung mithilfe des konzeptionellen Modells anstelle des relationalen Modells vornehmen. Entity Framework ordnet alle nach dem konzeptionellen Modell programmierten Befehle dem logischen Modell zu (siehe Abbildung 2).
Abbildung 2 Entwerfen des Entitätsdatenmodells (EDM) (Klicken Sie zum Vergrößern auf das Bild)
Das konzeptionelle Modell wird in einer XML-Datei definiert, die die konzeptionelle Schemadefinitionsprache (Conceptual Schema Definition Language, CSDL) verwendet. Mit CSDL werden die Entitäten und Beziehungen so definiert, wie sie auf der Geschäftsebene der Anwendung bekannt sind. Das logische Modell stellt das Datenbankschema dar und wird in einer XML-Datei definiert, die die Speicherschemadefinitionsprache (Store Schema Definition Language, SSDL) verwendet. Eine Entität in einem konzeptionellen Modell könnte z. B. die Daten aus mehreren Tabellen einer Datenbank ableiten. Zwischen dem konzeptionellen und dem logischen Modell besteht eine 1:1-Zuordnung der Entitäten. Die Leistungsstärke von EDM liegt jedoch darin, dass die Entitäten nicht 1:1 verknüpft werden müssen. Die Zuordnungsschicht wird durch die Zuordnungsschemasprache (Mapping Schema Language, MSL) definiert und ordnet die beiden anderen Schichten einander zu. Deshalb können Entwickler den Programmcode auf Grundlage des konzeptionellen Modells schreiben und diese Anweisungen dem logischen Modell zuordnen.
Generieren eines Entitätsdatenmodells
Sie können ein EDM mit einer Datenbank als Ausgangspunkt generieren. Anschließend können Sie die XML-Datei manuell ändern (evtl. steht in einer zukünftigen Version von Visual Studio dafür auch ein Modelliertool zur Verfügung). Wenn Sie Ihrem Projekt ein ADO.NET-EDM hinzufügen, führt Sie der Assistent durch den Prozess der EDM-Erstellung.
Nachdem Sie das EDM mithilfe der im Lieferumfang von „Orcas“ enthaltenen Northwind-Beispieldatenbank generiert haben, wird eine Liste der Objekte angezeigt, die entworfen werden können (siehe Abbildung 3). Ich habe alle Tabellen ins Modell eingefügt, damit der Assistent CSDL-, SSDL- und MSL-Dateien für alle Tabellen in Northwind generiert. Dies ist natürlich eine reine 1:1-Zuordnung zwischen Tabelle und Entität. Durch Kombinieren von Entitäten oder auch durch Vererbung kann ich diese Zuordnung so anpassen, dass sie meinen Geschäftsanforderungen entspricht.
Abbildung 3 Der EDM-Assistent (Klicken Sie zum Vergrößern auf das Bild)
Der Assistent generiert aus CSDL eine Reihe von Klassen, die das Domänenmodell darstellen. Einige dieser Klassen finden Sie in Abbildung 4 im Fenster „Klassenansicht“. Aufgrund der anfänglichen 1:1-Zuordnung gibt es zu jeder Tabelle eine Klasse. Der EDM-Assistent hat die Beziehung zwischen Customers und Orders in der Datenbank übernommen und eine entsprechende Zuordnung im konzeptionellen Modell erstellt. Daher enthält die Klasse „Customers“ eine Navigationseigenschaft namens „Orders“, über die Entwickler von einer Customers-Instanz zu einer beliebigen zugeordneten Orders-Instanz für einen Kunden navigieren können.
Abbildung 4 Klassen
Analyse von CSDL
Die in der CSDL-Datei vorhandenen Metadaten enthalten eine Liste mit Entitäten, die durch das Element „EntityType“ dargestellt werden sowie eine Liste der Beziehungen, die durch das Element „Association“, ein AssociationType, dargestellt werden. (Beachten Sie, dass es sich hier um ein inkonsistentes Benennungsschema in Beta 1 handelt.) Ein Fragment der zuvor von mir generierten CSDL-Datei sehen Sie in Abbildung 5. Die Entitäten enthalten eine Liste mit Skalareigenschaften, die die Entität definieren. Das Key-Attribut gibt an, welche Eigenschaften der Schlüssel für die Entität sind. Verbundschlüssel sind daran zu erkennen, dass Eigenschaftsnamen durch ein Leerzeichen voneinander getrennt sind. Entitäten können auch eine spezielle Eigenschaft mit der Bezeichnung „NavigationProperty“ enthalten. Diese definiert die Navigation von einer Entität zu einer anderen Entität mithilfe von Zuordnungen.

Figure 5 EntityType-Definition in CSDL
<EntityType Name=”Customers” Key=”CustomerID”>
<Property Name=”CustomerID” Type=”String” Nullable=”false”
MaxLength=”4000” FixedLength=”true” />
<Property Name=”CompanyName” Type=”String” Nullable=”false”
MaxLength=”4000” />
<Property Name=”ContactName” Type=”String” MaxLength=”4000” />
<Property Name=”ContactTitle” Type=”String” MaxLength=”4000” />
<Property Name=”Address” Type=”String” MaxLength=”4000” />
<Property Name=”City” Type=”String” MaxLength=”4000” />
<Property Name=”Region” Type=”String” MaxLength=”4000” />
<Property Name=”PostalCode” Type=”String” MaxLength=”4000” />
<Property Name=”Country” Type=”String” MaxLength=”4000” />
<Property Name=”Phone” Type=”String” MaxLength=”4000” />
<Property Name=”Fax” Type=”String” MaxLength=”4000” />
<NavigationProperty Name=”Orders”
Relationship=”NorthwindModel.FK_Orders_Customers”
FromRole=”Customers” ToRole=”Orders” />
</EntityType>
Der AssociationType zwischen Customer und den entsprechenden Orders wird in diesem CSDL-Fragment definiert:
<Association Name=”FK_Orders_Customers”>
<End Role=”Customers” Type=
”NorthwindModel.Customers”
Multiplicity=”0..1” />
<End Role=”Orders” Type=
”NorthwindModel.Orders” Multiplicity=”*” />
</Association>
Die Endelemente von AssociationType geben die Teilnehmer der Zuordnung an. In diesem Beispiel ist der Kundenentität (Customers) eine Auftragsentität (Orders) zugeordnet. Eine Kundenentität kann auch auf eine beliebige Anzahl Auftragsentitäten bezogen werden. Dies wird durch die Multiplizität festgelegt.
Während die Elemente „EntityType“ und „AssociationType“ die Arten der Domänenentitäten und Beziehungen definieren, legen die Elemente „EntitySet“ und „AssociationSet“ den Umfang fest. Alle „Sätze“, die logisch zusammen gruppiert werden sollten, sind in einem EntityContainer-Element enthalten. (Detailliertere Informationen zu CSDL finden Sie in der Datei „NorthwindEntities.csdl“ im zugehörigen Download).
Das folgende CSDL-Fragment zeigt das EntityContainer-Element und Teile seines Inhalts:
<EntityContainer Name=”NorthwindEntities”>
<EntitySet Name=”Customers”
EntityType=”NorthwindModel.Customers” />
<EntitySet Name=”Orders”
EntityType=”NorthwindModel.Orders” />
<AssociationSet Name=”FK_Orders_Customers”
Association=”NorthwindModel.FK_Orders_Customers”>
<End Role=”Customers” EntitySet=”Customers” />
<End Role=”Orders” EntitySet=”Orders” />
</AssociationSet>
</EntityContainer>
Dieses Fragment zeigt EntitySets für die EntityTypes „Customers“ und „Orders“. Auch AssociationSet FK_Orders_Customers wird hier deklariert. Dieses Fragment definiert die Entitäten „Customers“ und „Orders sowie die Beziehung zwischen beiden.
Zuordnung zum Speicher
Die SSDL-Datei definiert die Struktur der relationalen Daten in der Datenbank. In diesem Fall werden die XML-Elemente „EntityType“ und „AssociationType“ verwendet, um die Struktur der jeweils in der Datenbank vorhandenen Tabellen und Fremdschlüssel zu deklarieren. Der Namespace der SSDL-Datei wird auf Basis des in EDM verwendeten Datenbanknamens festgelegt. Das EntityContainer-Element wird jedoch nach dem Datenbankschema benannt. EntityContainer enthält eine Reihe von EntitySet- und AssociationSet-Elementen, die die Instanzen der Tabellen und Beziehungen deklarieren, die von EntityType und AssociationType dargestellt werden. Zu jedem EntitySet in der SSDL-Datei gibt es eine entsprechende Tabelle in der Datenbank.
Wenn Sie ein EDM von einer Datenbank generieren und unmittelbar danach die CSDL- und SSDL-Dateien öffnen, ohne Änderungen vorzunehmen, werden Sie eine auffallende Ähnlichkeit feststellen. Der Grund dafür ist, dass die Modelle direkt von der Datenbank generiert werden und somit das konzeptionelle Modell direkt dem logischen Speicher zugeordnet ist. Die MSL-Datei enthält die direkte Zuordnung von CSDL und SSDL. Alle anhand des EDM geschriebenen Abfragen werden in generierte SQL-Befehle übersetzt. Entity Framework unterstützt auch gespeicherte Prozeduren anstelle generierter SQL-Abfragen.
Mit einem EntityContainerMapping-Element wird dem Speicher (SSDL) ein Modell (CSDL) zugeordnet. Das Attribut „StorageEntityContainer“ gibt den Namen von EntityContainer im Speicher an, während das Attribut „EdmEntityContainer“ den entsprechenden EntityContainer im Modell angibt. Für die Zuordnung des EntitySet eines Modells zum EntitySet eines Speichers ist ein EntitySetMapping-Element erforderlich. Das Name-Attribut definiert den Namen des EntitySet im Modell, während das TableName-Attribut den Namen des entsprechenden EntitySet im Speicher definiert. Jede Eigenschaft im Modell ist über ein ScalarProperty-Element dem Speicher zugeordnet. Abbildung 6 zeigt ein MSL-Fragment.

Figure 6 Zuordnen der Vererbungsentität in MSL
<cs:EntitySetMapping cs:Name=”Products”>
<cs:EntityTypeMapping cs:TypeName=”NorthwindModel.Products”>
<cs:TableMappingFragment cs:TableName=”Products”>
<cs:ScalarProperty cs:Name=”ProductID” cs:ColumnName=”ProductID” />
<cs:ScalarProperty cs:Name=”ProductName”
cs:ColumnName=”ProductName” />
<cs:ScalarProperty cs:Name=”QuantityPerUnit”
cs:ColumnName=”QuantityPerUnit” />
<cs:ScalarProperty cs:Name=”UnitPrice” cs:ColumnName=”UnitPrice” />
<cs:ScalarProperty cs:Name=”UnitsInStock”
cs:ColumnName=”UnitsInStock” />
<cs:ScalarProperty cs:Name=”UnitsOnOrder”
cs:ColumnName=”UnitsOnOrder” />
<cs:ScalarProperty cs:Name=”ReorderLevel”
cs:ColumnName=”ReorderLevel” />
<!--<cs:ScalarProperty cs:Name=”Discontinued”
cs:ColumnName=”Discontinued” />-->
<cs:Condition cs:ColumnName=”Discontinued” cs:Value=”0”/>
</cs:TableMappingFragment>
</cs:EntityTypeMapping>
<!--</cs:EntitySetMapping>
<cs:EntitySetMapping cs:Name=”DiscontinuedProducts”>-->
<cs:EntityTypeMapping cs:TypeName=
”NorthwindModel.DiscontinuedProducts”>
<cs:TableMappingFragment cs:TableName=”Products”>
<cs:ScalarProperty cs:Name=”ProductID” cs:ColumnName=”ProductID” />
<cs:ScalarProperty cs:Name=”ProductName”
cs:ColumnName=”ProductName” />
<cs:ScalarProperty cs:Name=”QuantityPerUnit”
cs:ColumnName=”QuantityPerUnit” />
<cs:ScalarProperty cs:Name=”UnitPrice” cs:ColumnName=”UnitPrice” />
<cs:ScalarProperty cs:Name=”UnitsInStock”
cs:ColumnName=”UnitsInStock” />
<cs:ScalarProperty cs:Name=”UnitsOnOrder”
cs:ColumnName=”UnitsOnOrder” />
<cs:ScalarProperty cs:Name=”ReorderLevel”
cs:ColumnName=”ReorderLevel” />
<cs:Condition cs:ColumnName=”Discontinued” cs:Value=”1”/>
</cs:TableMappingFragment>
</cs:EntityTypeMapping>
</cs:EntitySetMapping>
Definition von Vererbung
EDM unterstützt auch Modelle, die nicht exakt 1:1 der Datenbank zugeordnet sind. Sie können beispielsweise mit der Northwind-Datenbank eine Klasse namens „DiscontinuedProducts“ erstellen, die zwar alle Eigenschaften der Klasse „Products“ erbt, jedoch nur solche Produkte enthält, bei denen die Spalte „Discontinued“ (Abgelaufen) den Wert 1 enthält. (Beachten Sie, dass die DiscontinuedProducts-Klasse auch weitere Eigenschaften aufweisen könnte.) Dieses stark vereinfachte Vererbungsszenario führt dennoch vor, wie Vererbung mithilfe von EDM implementiert werden kann.
Der erste Schritt beim Erstellen der DiscontinuedProducts-Klasse im konzeptionellen Modell besteht darin, die CSDL-Datei zu öffnen und einen neuen EntityType zu erstellen. Diesen nennen Sie DiscontinuedProducts und setzen das Attribut „BaseType“ auf NorthwindModel.Products (Schema- und grundlegender EntityType-Name). Der abgeleitete EntityType erbt die Eigenschaften des EntityType von Products, einschließlich seiner Schlüssel. Deshalb muss das Schlüsselattribut bzw. die Eigenschaften für den neu abgeleiteten EntityType nicht angegeben werden. Auch die Eigenschaft „Discontinued“ (Abgelaufen) kann im EntityType „Products“ auskommentiert werden. Dazu ist in der CSDL-Datei folgender zusätzlicher Code erforderlich:
<EntityType Name=”DiscontinuedProducts”
BaseType=”NorthwindModel.Products”/>
Als nächster Schritt in diesem Prozess öffnen Sie die MSL-Datei, suchen das Element „EntitySetMapping“ von Products und löschen dessen Attribute „TypeName“ und „TableName“. Diese werden nun für jeden EntityType gesondert festgelegt. Dann erstellen Sie ein untergeordnetes EntityTypeMapping-Element und setzen TypeName auf NorthwindModel.Products. Für jeden EntityType, der von einem Basis-EntityType erbt, muss EntitySetMapping ein EntityTypeMapping-Element umfassen. Erstellen Sie ein untergeordnetes EntityTypeMapping-Element mit Namen TableMappingFragment, und setzen Sie das Attribut TableName auf Products. Grundsätzlich wird mit diesen Schritten die Zuordnung des EntitySetMapping-Elements auf eine tiefere Ebene verlagert.
Kommentieren Sie die Zuordnung der Eigenschaft „Discontinued“ aus, und fügen Sie eine Bedingung (Condition) hinzu, die angibt, dass nur solche Datensätze berücksichtigt werden, deren Wert für „Discontinued“ gleich 0 ist. Kopieren Sie das gesamte XML-Fragment „EntityTypeMapping“, ändern Sie das Attribut „Name“ in „DiscontinuedProducts“, und ändern Sie den Wert der Bedingung in 1. Das neue MSL-Dateifragment ist in Abbildung 6 dargestellt.
Zuordnung über mehrere Tabellen
EDM unterscheidet sich noch in anderer Weise vom reinen 1:1-Modell zum Speichern der Zuordnungen. Eine einzelne Entität im Modell kann mehreren Tabellen im Speicher zugeordnet werden. Die Tabellen „Contacts“ (Kontakte) und „ContactNameSplit“ der Northwind-Beispieldatenbank haben eine 1:1-Beziehung und können in einer einzigen Entität im Modell zusammengefasst werden. Für dieses Beispiel erstelle ich eine Entität im Modell, die alle Spalten der Contacts-Tabelle sowie die Spalten „Title“ und „Name“ aus der ContactNameSplit-Tabelle umfasst.
Zuerst muss EntityType „Contacts“ in der CSDL-Datei so geändert werden, dass zwei weitere Eigenschaften enthalten sind: Name und Title.
Die weiteren Änderungen werden ausführlicher beschrieben. Die beiden neuen Eigenschaften im Modell müssen nun dem Speicher mit der MSL-Datei zugeordnet werden. Das EntitySetMapping-Element für das EntitySet „Contacts“ muss so geändert werden, dass eine Zuordnung zu mehreren Tabellen dargestellt wird. Für dieses Beispiel wurden im vorhandenen EntitySetMapping-Tag für das Contacts-EntitySet die Attribute „TableName“ und „TypeName“ gelöscht. Diese Attribute sind im EntitySetMapping-Element nur deklariert, wenn EntitySet im Modell 1:1 einem EntitySet im Speicher zugeordnet ist.
Da die Zuordnung zwischen dem EntitySet des Modells und dem des Speichers für die Kontakte gelöscht wurde, muss ein Ersatz erstellt werden. Ein untergeordnetes Element namens „EntityTypeMapping“ ersetzt die Zuordnung. Sie müssen zwei dieser Elemente erstellen, eins für Contacts und eins für ContactNameSplit aus dem Speicher. Die EntityTypeMapping-Elemente definieren das TypeName-Attribut für jedes der beiden EntitySets.
In jedem EntityTypeMapping-Element befindet sich ein untergeordnetes Element mit dem Namen „TableMappingFragment“. Dieses Element enthält das TableName-Attribut, das dem EntitySet des Speichers entspricht. In TableMappingFragment werden alle ScalarProperty-Elemente definiert, die die Modelleigenschaften dem Speicher zuordnen. Abbildung 7 zeigt die überarbeitete EntitySetMapping von Contacts, die nun die Tabellen „Contacts“ und „ContactSplitName“ aus dem Speicher einem einzigen EntitySet im Modell zuordnet.

Figure 7 Zusammenfassen von Tabellen in einer Entität
<cs:EntitySetMapping cs:Name=”Contacts”>
<cs:EntityTypeMapping cs:TypeName=”NorthwindModel.Contacts”>
<cs:TableMappingFragment cs:TableName=”Contacts”>
<cs:ScalarProperty cs:Name=”ContactID”
cs:ColumnName=”ContactID” />
<cs:ScalarProperty cs:Name=”ContactType”
cs:ColumnName=”ContactType” />
<cs:ScalarProperty cs:Name=”CompanyName”
cs:ColumnName=”CompanyName” />
<cs:ScalarProperty cs:Name=”ContactName”
cs:ColumnName=”ContactName” />
<cs:ScalarProperty cs:Name=”ContactTitle”
cs:ColumnName=”ContactTitle” />
<cs:ScalarProperty cs:Name=”Address” cs:ColumnName=”Address” />
<cs:ScalarProperty cs:Name=”City” cs:ColumnName=”City” />
<cs:ScalarProperty cs:Name=”Region” cs:ColumnName=”Region” />
<cs:ScalarProperty cs:Name=”PostalCode”
cs:ColumnName=”PostalCode” />
<cs:ScalarProperty cs:Name=”Country” cs:ColumnName=”Country” />
<cs:ScalarProperty cs:Name=”Phone” cs:ColumnName=”Phone” />
<cs:ScalarProperty cs:Name=”Extension”
cs:ColumnName=”Extension” />
<cs:ScalarProperty cs:Name=”Fax” cs:ColumnName=”Fax” />
<cs:ScalarProperty cs:Name=”PhotoPath”
cs:ColumnName=”PhotoPath” />
</cs:TableMappingFragment>
</cs:EntityTypeMapping>
<cs:EntityTypeMapping cs:TypeName=”ContactNameSplit”>
<cs:TableMappingFragment cs:TableName=”ContactNameSplit”>
<cs:ScalarProperty cs:Name=”ContactID” cs:ColumnName=”ID” />
<cs:ScalarProperty cs:Name=”Name” cs:ColumnName=”Name” />
<cs:ScalarProperty cs:Name=”Title” cs:ColumnName=”Title” />
</cs:TableMappingFragment>
</cs:EntityTypeMapping>
Verwenden von EntityClient
Der Zugriff auf das konzeptionelle Modell von Entity Framework erfolgt auf drei verschiedene Arten (siehe Abbildung 1). An dieser Stelle möchte ich EntityClient, den neuen .NET-Datenanbieter einführen.
EntityClient wird vom logischen Speicher abstrahiert, da er über eine eigene textbasierte Sprache, nämlich Entity SQL, mit dem konzeptionellen Modell kommuniziert. Alle mithilfe von EntityClient ausgeführten Entity SQL-Abfragen werden in Befehlsstrukturen kompiliert, die an den Speicher gesendet werden. Die Konvertierung der Abfragen von Entity SQL über das konzeptionelle Modell zum Speicher erfolgt mit Entity Framework.
Die Klassen in EntityClient sind den Klassen bei den üblichen ADO.NET-Anbietern ähnlich. EntityClient-Abfragen werden z. B. mit einem EntityCommand-Objekt ausgeführt, das für die Verbindung zu EDM ein EntityConnection-Objekt erfordert. Obwohl EntityClient mit den Entitäten in EDM interagiert, werden keine Instanzen der Entitäten zurückgegeben, sondern stattdessen alle Ergebnisse in einem DbDataReader-Objekt dargestellt. EntityClient kann entweder einen standardmäßigen Zeilen- und Spaltensatz oder eine Darstellung komplexerer hierarchischer Daten über DbDataReader zurückgeben.
Abbildung 8 zeigt anhand eines Beispiels, wie mithilfe von EntityClient eine Verbindung zum konzeptionellen Modell hergestellt und eine Liste der Kunden in London abgerufen wird. EntityConnection kann eine komplette Verbindungszeichenfolge zur konzeptionellen Schicht bzw. den Namen der Verbindungszeichenfolge in die App.Config-Datei übernehmen. Die Verbindungszeichenfolge enthält eine Liste der Metadatendateien (CSDL-, MSL- und SSDL-Dateien) sowie die datenbankspezifischen Informationen der Verbindungszeichenfolgen für den Speicher. Ein Beispiel für die komplette Verbindungszeichenfolge in Abbildung 8 sehen Sie hier:

Figure 8 Entity Client und Entity SQL zum Abrufen für EDM
string city = “London”;
using (EntityConnection cn =
new EntityConnection(“Name=NorthwindEntities”))
{
cn.Open();
EntityCommand cmd = cn.CreateCommand();
cmd.CommandText =
“SELECT VALUE c FROM NorthwindEntities.Customers “ +
“AS c WHERE c.City = @city”;
cmd.Parameters.AddWithValue(“city”, city);
DbDataReader rdr = cmd.ExecuteReader(
CommandBehavior.SequentialAccess);
while (rdr.Read())
Console.WriteLine(rdr[“CompanyName”].ToString());
rdr.Close();
}
“metadata=.\NorthwindEntities.csdl|.\NorthwindEntities.ssdl|.
\NorthwindEntities.msl;provider=System.Data.SqlClient;provider connection string=’Data Source=DDVPC01
\SQLEXPRESS;Initial Catalog=Northwind;
Integrated Security=True’”
Der Code in Abbildung 8 zeigt, wie Sie ein EntityConnection-Objekt erstellen und damit ein EntityCommand ausführen können. Die mit Entity SQL geschriebene Abfrage bezieht sich auf Customers EntitySet in EDM. Hinweis: Die Ähnlichkeit mit der Syntax von T-SQL ist beabsichtigt. (Eine nützliche Referenz der Entity SQL-Syntax finden Sie in der MSDN®-Dokumentation zur Beta 1-Veröffentlichung von „Orcas“.)
Wie in Abbildung 8 dargestellt können auch EntityParameter-Objekte hinzugefügt werden. In Beta 1 unterstützt EntityClient keine DML-Abfragen. Es wird jedoch für künftige Versionen daran gearbeitet. Dennoch kann DML mit anderen Mitteln, z. B. LINQ to Entities, ausgeführt werden.
Objektdienste
Mit den Objektdiensten steht Ihnen eine weitere Möglichkeit der Interaktion mit den in EDM dargestellten Daten zur Verfügung. Mit Objektdiensten können Sie Objekte laden und alle in EDM definierten Beziehungen navigieren. In Abbildung 1 sehen Sie, wie mit den Objektdiensten über EntityClient Daten abgerufen werden. Die Objektdienste fügen Identity Resolution (Identitätsauflösung) hinzu. Dies ist bei Verwendung eines DataSet ein manueller Prozess. Außerdem bieten sie Objektdauerhaftigkeit und Änderungsverfolgung bei Ereignissen und ermöglichen so explizites Laden und Speichern. Dadurch werden Roundtrips zum Server reduziert.
Mit den Objektdiensten können Objektlisten direkt zurückgegeben werden, d. h. es können sowohl Projektionen als auch definierte Entitäten zurückgegeben werden. Sie könnten beispielsweise eine Liste<Customers> abrufen, die in EDM definiert wurde. Sie können Kundenobjekte untersuchen, deren Werte ändern und die Daten wieder in der Datenbank speichern.
Wenn Sie Projektionen mit den Objektdiensten verwenden, sind die zurückgegebenen Daten kein aktualisierbares Objekt. Da Projektionen nur bestimmte Eigenschaften von Entitäten und nicht die gesamte Entität zurückgeben, können Aktualisierungen der projizierten Daten mit den Objektdiensten nicht in die Datenbank zurück gespeichert werden. Wenn Sie beabsichtigen, die Daten zu aktualisieren, sollten Sie die gesamte Entität zurückgeben und keine Projektion verwenden.
Mit den Objektdiensten können Sie Abfragen in Entity SQL durchführen oder mithilfe von LINQ to Entities Abfragen erstellen. Im folgenden Beispiel werden Abfragen in Entity SQL durchgeführt, um eine Kundenliste abzurufen:
string city = “London”;
ObjectQuery<Customers> query = northwindContext.CreateQuery<Customers>(
“SELECT VALUE c FROM Customers AS c WHERE c.City = @city”,
new ObjectParameter(“city”, city));
foreach (Customers c in query) Console.WriteLine(c.CompanyName);
In EDM wird EntityContainer durch eine Klasse dargestellt, die von ObjectContext erbt (in diesem Beispiel northwindContext). Die ObjectContext-Klasse implementiert die ObjectQuery<T>-Schnittstelle, über die Abfragen in Entity SQL oder LINQ erstellt werden können.
Die CreateQuery-Methode akzeptiert eine parametrisierte Entity SQL-Anweisung, die eine Abfrage zum Abrufen einer Liste mit Kundenentitäten definiert. Die eigentliche SQL-Anweisung, die auf die Datenbank zugreift, wird ausgeführt, wenn ObjectQuery<Customers> unter Verwendung einer foreach-Anweisung wiederholt wird.
LINQ to Entities
Dynamische Abfragen können in Entity SQL geschrieben und mit den Objektdiensten zur Interaktion mit EDM-Entitäten verwendet werden. Entity Framework kann jedoch auch mit streng typisierten EDM-Klassen und LINQ to Entities verwendet werden. Im zuvor gezeigten Beispiel kann die Abfrage mit den Objektdiensten und Entity SQL so geändert werden, dass sie mit LINQ to Entities ausgeführt wird:
string city = “London”;
var query = from c in northwindContext.Customers
where c.City == city
select c;
foreach (Customers c in query) Console.WriteLine(c.CompanyName);
In diesem Codebeispiel wird die textbasierte Syntax von Entity SQL durch die streng typisierte LINQ-Syntax ersetzt, die von C# 3.0 unterstützt wird. Weitere Informationen zu LINQ und entsprechenden Support für C# und Visual Basic
® finden Sie in der Ausgabe des
MSDN Magazins von Juni 2007 unter
msdn.microsoft.com/msdnmag/issues/07/06.
Rufen Sie sich noch einmal ins Gedächtnis zurück, dass EntityType in EDM unter dem Namen „DiscontinuedProducts“ erstellt wurde und von Products erbt. Die Vererbungsfeatures von EDM können sowohl mit LINQ als auch mit den Objektdiensten zum Abrufen einer Liste für ausgelaufene Produkte verwendet werden. Beachten Sie, dass das folgende Beispiel nicht angibt, an welcher Stelle „Discontinued“ den Wert 1 enthält. Stattdessen wird der Typ der Produktentität anhand des abgeleiteten EntityType von DiscontinuedProducts geprüft. Dieser verwendet wiederum die bei der Zuordnung (MSL-Datei) erstellte Bedingung I, um eine geeignete SQL-Anweisung zum alleinigen Abrufen der ausgelaufenen Produkte zu generieren:
var query = from p in northwindContext.Products
where p is DiscontinuedProducts
select p;
foreach (Products p in query) Console.WriteLine(p.ProductName);
Sie können auch Abfragen erstellen, die die integrierten Beziehungen in EDM nutzen (definiert durch AssociationSet). Zum Beispiel kann mithilfe des folgenden LINQ-Abfrageausdrucks eine Liste der Aufträge von Kunden in London abgerufen werden:
var query = from o in northwindContext.Orders
where o.Customers.City == “London”
select o;
foreach (Orders o in query) Console.WriteLine(o.OrderID);
Dieses Codebeispiel beginnt mit der Auftragsentität und nutzt die Navigationseigenschaft namens „Customers“ zum Prüfen der Eigenschaft „City“. Die Aufträge der Kunden, die nicht in London wohnen, werden komplett herausgefiltert. Da eine Liste mit Auftragsentitäten zurückgegeben wird, können die Entitäten geändert und diese Änderungen in der Datenbank gespeichert werden. Das Speichern der Änderungen in der Datenbank erfolgt über die SaveChanges-Methode.
Im folgenden Beispiel wird die Liste der Kunden in London abgerufen und die Eigenschaft „Country“ für jeden Kunden auf United Kingdom gesetzt. Die Änderungen werden zwar dauerhaft in StateManager gespeichert, jedoch erst dann in die Datenbank geschrieben, wenn die SaveChanges-Methode ausgeführt wurde:
var query = from c in northwindContext.Customers
where c.City == “London”
select c;
foreach (Customers c in query) c.Country = “United Kingdom”;
northwindContext.SaveChanges();
Sie können auch eine neue Instanz einer Entität erstellen und diese mit der AddObject-Methode von ObjectContext zu EDM hinzufügen. Im folgenden Beispiel sehen Sie, wie Sie der Kategorientabelle in der Datenbank eine neue Kategorie hinzufügen können:
Categories newCat = new Categories();
newCat.CategoryName = “Other”;
northwindContext.AddObject(newCat);
northwindContext.SaveChanges();
int newCatID = newCat.CategoryID;
Zuerst wird eine Instanz der Category-Entität erstellt und die Eigenschaft „CategoryName“ festgelegt. Dann wird die neue Kategorie dem EDM mithilfe der AddObject-Methode hinzugefügt. Nach Aufruf der SaveChanges-Methode wird eine SQL-Anweisung generiert, die die neue Kategorie in die Datenbank einfügt und die CategoryID für die neue Zeile zurückgibt.
Wenn Beziehungen zwischen Entitäten bestehen, möchten Sie möglicherweise die neue Entität einer vorhandenen Entität zuordnen. Sie können z. B. eine neue Auftragsentität erstellen und die Kundeneigenschaft auf eine vorhandene Kundenentität setzen. Obwohl die Auftragstabelle in der Datenbank über eine Spalte „CustomerID“ verfügt, stellt EDM die Beziehung in einem objektorientierten Ansatz dar und navigiert mithilfe einer Kundeneigenschaft zur entsprechenden Kundenentität:
Orders newOrder = new Orders();
newOrder.OrderDate = DateTime.Today;
Customers cust = northwindContext.Customers.Where(
“it.CustomerID = ‘ALFKI’”).First();
newOrder.Customers = cust;
northwindContext.AddObject(newOrder);
northwindContext.SaveChanges();
Zusammenfassung
Entity Framework ermöglicht Entwicklern, Daten über ein Objektmodell statt eines logischen/relationalen Datenmodells zu behandeln. Wenn das EDM entworfen und einem relationalen Speicher zugeordnet ist, kann mit den Objekten unter Verwendung verschiedener Verfahren einschließlich EntityClient, ObjectServices und LINQ interagiert werden.
Herkömmliche Objekte wie DataSet, DataAdapter, DbConnection und DbCommand werden zwar in der nächsten Version von ADO.NET in Visual Studio „Orcas“ weiterhin unterstützt, doch bietet Entity Framework wichtige Ergänzungen, die neue, interessante Möglichkeiten für ADO.NET eröffnen.
Senden Sie Fragen und Kommentare für John Papa in englischer Sprache an mmdata@microsoft.com.
John Papa ist leitender .NET-Berater bei ASPSOFT (
aspsoft.com). Als leidenschaftlicher Baseballfan feuert er an den meisten Sommerabenden zusammen mit seiner Familie und seinem Hund Kadi die Yankees an. John, ein C#-MVP, hat mehrere Bücher über ADO, XML und SQL Server verfasst. Er hält häufig Vorträge auf Branchenkonferenzen, wie z. B. VSLive und führt einen Blog unter
codebetter.com/blogs/john.papa bzw. unter
www.johnpapa.net.