10.07.2010, 15:36 Uhr
Entity Framework 4.0 – neuer Anlauf für den Datenlayer der Zukunft
Mit .NET 4.0 wurde auch das Entity Framework komplett überarbeitet. Microsofts Standard-ORM besitzt damit viele neue Funktionalitäten und zudem eine strategische Bedeutung. Das Entity Framework ist der Name für den Objektrelationalen Mapper (ORM) von Microsoft, der relationale Datenbanken über eine Objektschicht anspricht und die aus Objekten bestehende Geschäftslogik einer Anwendung direkt in einer Datenbank persistieren kann.
Objektrelationale Mapper gibt es seit vielen Jahren. Sie haben sich sehr schnell zu einem unverzichtbaren Bestandteil einer modernen Geschäftsanwendung entwickelt. Zu groß sind die Vorteile, die sich durch die Möglichkeit ergeben, die Geschäftsobjekte einer Anwendung direkt in einer relationalen Datenbank speichern zu können, ohne sich Gedanken über das Abbilden der Klassenstruktur auf die Tabellenstruktur der Datenbank machen oder den stets sehr ähnlichen Zugriffscode immer wieder neu schreiben zu müssen. Microsoft kam nicht nur relativ spät mit einem eigenen ORM, die erste Version des Entity Framework, die mit dem Service Pack 1 von .NET 3.5 und VS 2008 eingeführte wurde, konnte zudem nicht alle Entwickler überzeugen. Außerdem wurde vom Hersteller mit LINQ to SQL kurz vor zuvor mit VS 2008 bereits ein ORM ausgeliefert, der etwas einfacher gestrickt ist, nur auf SQL Server-Datenbanken beschränkt ist und zudem nicht weiterentwickelt werden soll. Mit Entity Framework 4.0 (EF4) liegt die zweite Version des offiziellen ORM von Microsoft vor, das um zahlreiche wichtige Funktionalitäten erweitert wurde (siehe Tabelle am Ende des Artikels).Provider Modell sorgt für Flexibilität
Abbildung 1 zeigt den Aufbau des Entity Framework. Die Grundidee ist, dass eine (beliebige) Datenbank über einen Entitätscontainer angesprochen wird, der die Tabellen der Datenbank und deren Beziehungen enthält. Im Mittelpunkt des Containers steht das konzeptionelle Modell. Es ist eine XML-Struktur, die die vom Entwickler ausgewählten Tabellen als Entitäten definiert. Das konzeptionelle Modell basiert nicht direkt auf dem Datenbankschema, sondern auf dem Speichermodell, das ebenfalls ein Teil des Containers. Es umfasst die mit Hilfe des Designers aus der Datenbank ausgewählten Tabellen und Abfragen aus denen der Entitätscontainer abgeleitet wird. Der Mapping-Bereich stellt als dritter Teil des Entitätscontainers die Verbindung zwischen den Entitäten im Speichermodell und jenen im konzeptionellen Modell her. Während die meisten ORMs die Tabellen der Datenbank direkt auf eine Klassenstruktur abbilden, verwendet Entity Framework mit dem konzeptionellen Modell eine zusätzliche Ebene, die für mehr Flexibilität sorgen soll.
Abbildung 1: Das Entity Framework besteht aus mehreren Schichten
Der wichtigste Aspekt beim Entity Framework ist, dass es auf dem Providermodell von ADO.NET 2.0 aufsetzt und damit prinzipiell datenbankunabhängig ist. In .NET 4.0 werden SQL Server (ab Version 2005) und SQL Server Compact-Datenbanken unterstützt (allerdings nicht in vollem Umfang -der Wert eines automatisch generierten Primärschlüsselfeldes steht nach dem Abspeichern eines neu hinzugefügten Objekts danach leider nicht über dieses Objekt zur Verfügung). Weitere Provider, z.B. für Oracle, werden von Drittanbietern wie z.B. DevArt zur Verfügung gestellt.Der Entitätscontainer wird über einen eigenen ADO.NET-Provider mit dem Namen Entity Client angesprochen. Es gibt generell drei Alternativen, um über den Entity Client die Daten einer Datenbank anzusprechen: 1. Über die Object Services, die für jede Tabelle eine Property über jenes Objekt zur Verfügung stellen, das den Entity Container repräsentiert. 2. Gewohnt komfortabel über LINQ to Entities. 3. Über Entity SQL, einem von Standard-SQL abgeleiteten SQL mit "objektorientierten" Erweiterungen, wenn die Performance eine Rolle spielt. In drei Schritten zum Datenlayer
Auch wenn die Anzahl der Schritte zu einem Datenlayer definitiv kein Maßstab für die Qualität ist, ist es trotzdem beeindruckend wie einfach man auch beim Entity Framework zu einem funktional funktionstüchtigen Datenlayer gelangt. Dies soll im Folgenden am Beispiel der Northwind-Datenbank demonstriert werden.
Schritt 1: Auswahl der Vorlage "ADO.NET Entity Data Model", die auch bei den Express Editionen von Visual Studio 2010 zur Verfügung steht.
Abbildung 2: Für ein Entitätsmodell gibt es eine eigene VorlageSchritt 2: Auswahl der Datenverbindung. Die Verbindungszeichenfolge wird per Voreinstellung in App.Config gespeichert (der gewählte Name legt gleichzeitig den Namen des Entity-Containers fest, über den dieser später im Programm angesprochen wird). Schritt 3: Auswahl der Tabellen, für dieses Beispiel Products und Suppliers, und gegebenenfalls der Views und gespeicherten Prozeduren. Die Einstellung "Generierte Objektnamen in den Singular oder Plural setzen", die neu ist bei VS 2010, sorgt dafür, dass der Designer für die Namen von Klassen und Properties automatisch die passende Einzahl-/Mehrzahlform verwendet - die Klasse, die Products repräsentiert, heißt dann Product. Auch wenn dies aktuell nur bei englischsprachigen Namen funktioniert (die dafür zuständige Funktionalität ist austauschbar), ist dies eine praktische Angelegenheit.
Abbildung 3: Die Option sorgt für eine Anpassung der Klassen- und Property-Namen
Damit ist der Datenlayer bereits fertig. Mit dem Schließen des Designerfensters werden die Klassen in der Codebehind-Datei generiert. Der Codegenerator kann auf der Basis von T4-Templates ausgetauscht werden. Dazu muss lediglich im EF-Designer mit der rechten Maustaste über "Neues Codegenerierungselement hinzufügen" ein Template ausgewählt werden - von Anfang zur Auswahl stehen "ADO.NET Entity Object Generator" und "ADO.NET Entitäts-Generator mit Selbstnachverfolgung". Die dadurch eingefügte Vorlagendatei (Erweiterung .tt) generiert beim Erstellen aus der Edmx-Datei ein Klassenmodell, das die im Modell enthaltenen Entitäten repräsentiert.Der Zugriff auf die Datenbanken erfolgt über die Klasse, die den gesamten Entity Framework-Container repräsentiert (sie entspricht dem ObjectContext bei LINQ to SLQ). Wird sie instanziert, stehen die Datensätze der ausgewählten Tabellen über Properties zur Verfügung. Der folgende Befehl listet über eine LINQ to Entities-Abfrage alle Produkte auf, die aus Schweden stammen:IQueryable SwedProducts = from p in db. Productswhere p.SupplierID == (from s in db.Suppliers where s.Country == "Sweden"select s.SupplierID).FirstOrDefault()select p;
Deutlich kürzer wird die Abfrage, wenn zwischen beiden Tabellen eine Beziehung über das gemeinsame Feld SupplierID existiert, die im Entity-Designer auch nachträglich hinzugefügt werden kann, wenn sie in der Datenbank nicht existiert:IQueryable SwedProducts = from p in db.Productswhere p.Supplier.Country == "Sweden"select p;Auch wenn LINQ durch seine Eleganz besticht, manchmal ist das Argument Performance noch ein wenig bestechender. Für diesen Fall kann eine Abfrage direkt per Entity SQL durchgeführt werden, das sich als ein providerneutraler Dialekt von Standard-SQL versteht und sich von diesen durch zahlreiche Kleinigkeiten unterscheidet. Die folgende Entity SQL-Abfrage gibt alle Produkte zurück:Select value pfrom NwEntities.PRODUCTs as p
Listing 1 gibt ebenfalls die Namen aller Produkte eines bestimmten Herstellerlandes aus. Ob die Abfrage gegen eine Microsoft SQL Server- oder Oracle SQL Server-Datenbank ausgeführt wird spielt, den jeweiligen Provider vorausgesetzt, keine Rolle.using (EntityConnection cn = new EntityConnection(db.Connection.ConnectionString)) {cn.Open();EntityCommand cmd = cn.CreateCommand();cmd.CommandText = "Select value p from NwEntities.Products as p Join NwEntities.Suppliers As s On p.SupplierID=s.SupplierID Where s.Country = 'Sweden'";// Zu Anschauungszwecken den SQL-String ausgebenConsole.WriteLine(cmd.ToTraceString());EntityDataReader dr = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);while (dr.Read()) Console.WriteLine(dr.GetString(dr.GetOrdinal("PRODUCTNAME")));}
Listing 1: Eine Entity SQL-Abfrage mit einem Join zweier Tabellen
Mit EF 4.0 wurden wichtige Neuerungen eingeführt, die in der Tabelle zusammengestellt sind. Einen sehr guten Überblick gibt ein WebCast von Microsoft-Technologieberater Darius Quatscht, der unter [1] zur Verfügung steht. Ein ca. 500 Seiten starkes eBook, das sich allerdings noch auf die Version 1.0 bezieht, sei als ergänzende Lektüre empfohlen [2].Tabelle: Wichtige Neuerungen bei EF 4
>Unterstützung für Lazy Loading - über eine Naviationsproperty zur Verfügung gestellte Datensätze werden erst dann abgerufen, wenn sie benötigt werden.>Unterstützung für Plain Old CLR Objects (POCO). Auch Objekte, die sich nicht auf einer Datenbanktabelle ableiten, können in der Datenbank persistiert werden, wobei die Object Services z.B. auch Änderungen nachverfolgen.>Model First Design. Zuerst das Modell, dann wird aus dem Model eine Datenbank abgeleitet (EF 4 bietet aber kein "Round Trip Engineering").>Foreign Key-Spalten - auf Wunsch werden die Datensätze, die über eine Fremdschlüsselbeziehung angesprochen werden, über die Fremdschlüsselspalte und nicht über die Navigations-Property angesprochen, so dass um den Wert der Fremdschlüsselspalte zu erhalten die Datensätze der Fremdschlüsseltabelle nicht abgerufen werden müssen.>Erweiterte Unterstützung für Stored Procedures - endlich kann man auch Stored Procedure mit einem Rückgabewert von Typ Void oder Skalar ohne Einschränkungen benutzen. Hinzugekommen ist ein neuer Rückgabewert, der Complex Type. Dieser erlaubt es für einen Rückgabewert der nicht skalar ist, aber auch auf keine Entitätsklasse passt, einen komplexen Datentyp anzulegen. Dieser komplexe Datentyp kann im Entitätsmodell z.B. als Attribut einer Entitätsklasse eingefügt werden.Links[1] http://www.microsoft.com/germany/msdn/webcasts/library.aspx?id=1032445954[2] http://weblogs.asp.net/zeeshanhirani/archive/2008/12/18/my-christmas-present-to-the-entity-framework-community.aspx#comments
Peter Monadiemi