MSDN Magazin > Home > Ausgaben > 2008 > February >  Datenpunkte: Entwerfen eines Entitätsdaten...
Datenpunkte
Entwerfen eines Entitätsdatenmodells
John Papa

Codedownload verfügbar unter: DataPoints2008_02.exe (174 KB)
Browse the Code Online
Entity Framework ist eine hochinteressante neue Technologie, die für ADO.NET entwickelt wurde. Es ermöglicht Entwicklern, Daten nach einem logischen Modell statt nach einem physischen Modell anzuzeigen, und bietet ihnen dadurch mehr Flexibilität. In der Ausgabe von „Datenpunkte“ vom Juli 2007 habe ich eine ausführliche Übersicht über Entity Framework geboten, das in der ersten Hälfte des Jahres 2008 veröffentlicht wird. Wenn Sie mit Entity Framework noch nicht vertraut sind, lesen Sie meine Übersicht unter msdn.microsoft.com/msdnmag/issues/07/07/DataPoints.
Den Kern von Entity Framework bildet das Entitätsdatenmodell (EDM). Das EDM definiert die Entitätstypen, Beziehungen und Container, mit denen die Entwickler über Code interagieren. Entity Framework ordnet diese Elemente dem Speicherschema zu, das durch eine relationale Datenbank verfügbar gemacht wird. Das EDM wird für Entity Framework über XML-Code verfügbar gemacht, der das konzeptionelle Anwendungsmodell definiert. Das konzeptionelle Modell kann entweder separat definiert werden oder gemeinsam mit XML-Code, der das Speicherschema definiert, sowie mit XML-Code, der die Zuordnung zwischen beidem definiert. Obwohl es möglich (und manchmal auch notwendig) ist, den XML-Code manuell zu bearbeiten, ist es wesentlich einfacher, ein Entitätsmodell und eine Zuordnung mithilfe des neuen visuellen EDM-Designertools zu erstellen und zu ändern.
Im Artikel dieses Monats werde ich erläutern, wie Sie mithilfe des neuen visuellen EDM-Designertools ein Entitätsmodell entwickeln und den zugrunde liegenden XML-Code ändern können, der sowohl das Modell als auch die Zuordnung definiert. Ich werde zunächst alle Bestandteile beschreiben, die innerhalb von Entity Framework interagieren (einschließlich LINQ), und anschließend darauf eingehen, welche Rolle EDM dabei spielt. Darüber hinaus werde ich demonstrieren, wie mithilfe des visuellen Designertools ein Entitätsmodell und die zugehörigen Zuordnungen erstellt werden können. Zum Schluss werde ich Ihnen mehrere Fenster vorstellen, die Ihnen dabei helfen können, das Modell und die Zuordnungen zu ändern und zu untersuchen.
Ich werde in diesem Artikel die Rollen der verschiedenen Komponenten des EDM (z. B. EntityType und Association) erläutern. Die in diesem Artikel enthaltenen Beispiele zeigen, wie grundlegende Entitäten erstellt werden. Beachten Sie, dass in allen Beispielen dieses Artikels Visual Studio® 2008 und das entsprechende Entity Framework Beta 3 verwendet werden, die separat installiert werden müssen. Dabei verwende ich die überarbeitete Northwind-Datenbank, die den Beispielen für Beta 3 beigefügt ist.

Erläuterungen zum EDM
Bevor ich darauf eingehe, wie ein Entitätsmodell erstellt und verwaltet wird, möchte ich zunächst erklären, was das EDM ist und wie es mit den anderen Elementen von Entity Framework interagiert. Entity Framework besteht aus vielen Teilen einschließlich der EDM-Spezifikation und zugehörigen Zuordnungen, mit dem EDM interagierenden APIs sowie Tools, die dabei helfen, das Entitätsmodell und die Zuordnungen zu definieren und zu verwalten. Sobald Sie ein Entitätsmodell entwickelt haben, können Sie mithilfe der verschiedenen APIs wie z. B. dem EntityClient-Anbieter oder Object Services (einschließlich LINQ to Entities) Code dafür schreiben.
Der EntityClient-Datenanbieter arbeitet nach einem ähnlichen Modell wie herkömmliche ADO.NET-Objekte, da er EntityConnection- und EntityCommand-Objekte verwendet, um ein DbDataReader-Element zurückzugeben. Die Befehle für den EntityClient-Anbieter werden mithilfe von Entity SQL geschrieben, das T-SQL ähnelt, aber nicht Datenbankobjekte, sondern die im Entitätsmodell definierten Entitäten und die durch Object Services erstellten Objekte bearbeitet. Sie können über Object Services mit dem EDM interagieren, indem Sie entweder Entity SQL oder LINQ to Entities verwenden. Durch Object Services können Sie die vom konzeptionellen Modell generierten Klassen nutzen, die Features und Merkmale wie beispielsweise streng typisierte Objekte und Persistenz bieten (siehe Abbildung 1).
Abbildung 1 Übersicht über Entity Framework 
Diese Datenzugriffsverfahren ermöglichen Ihnen, mit den innerhalb des Entitätsmodells definierten konzeptionellen Entitäten statt mit den Objekten eines physischen Speichers wie beispielsweise einer relationalen Datenbank zu interagieren. Das Datenmodell und die zugehörigen Zuordnungen werden entweder mithilfe des visuellen Designertools oder durch manuelles Bearbeiten des XML-Codes erstellt, durch den sie definiert werden. Entity Framework, das in Abbildung 2 zu sehen ist, stellt das Bindeglied zwischen einer Anwendung und ihrer Datenbank bereit. Das EDM wird dazu verwendet, die Geschäftsentitäten durch das konzeptionelle Entitätsmodell, durch Entity Framework und durch die Zuordnungsspezifikation zu beschreiben, und übersetzt dies anschließend in den physischen Speicher der Tabellen, Ansichten, Funktionen und Prozeduren der Datenbank.
Abbildung 2 Entity Framework verbindet eine Anwendung mit ihrer Datenbank (Klicken Sie zum Vergrößern auf das Bild)
Das Entitätsmodell der Anwendung wird mithilfe der konzeptionellen Schemadefinitionsprache (Conceptual Schema Definition Language, CSDL) beschrieben. CSDL ist ein XML-Format, das die Entitäten und die Zuordnungen zwischen Entitäten definiert, mit denen die Entwickler über eine API wie z. B. LINQ to Entities interagieren werden. Entity Framework verwendet auch die Speicherschemadefinitionssprache (Store Schema Definition Language, SSDL), wobei es sich um ein XML-Format handelt, das das Speicherschema der relationalen Datenbank und die Zuordnungsschemasprache (Mapping Schema Language, MSL) definiert, um zu übersetzen, wie die Entitäten der CSDL dem durch die SSDL beschriebenen Speicherschema zugeordnet werden.
Die CSDL ist der Punkt, an dem der Entwickler den größten Einfluss besitzt, da hier die Entitäten definiert werden, mit denen er am häufigsten interagieren wird. Abbildung 2 zeigt, dass einige Entitäten direkt einer einzelnen Tabelle in einer Datenbank zugeordnet sein können, während andere Entitäten mehreren Tabellen zugeordnet sein können. Diese Entitätsfestlegung wird vom Entwicklungsteam auf Grundlage des Geschäftsmodells vorgenommen. Das Geschäftsmodell arbeitet oft mit einer einzelnen Entität, die in mehreren physischen Tabellen in der Datenbank vorhanden ist. Sie können in Abbildung 2 auch sehen, dass eine Entität einer Datenbankansicht zugeordnet werden kann, oder dass eine Entität eine Methode abrufen kann, die dann wiederum eine gespeicherte Prozedur aufruft. Entitäten können auch von anderen Entitäten abgeleitet werden, indem im konzeptionellen Modell mit Vererbung gearbeitet wird. Dies sind nur einige der Möglichkeiten, das Entitätsmodell mithilfe der EDM-Designertools zu entwerfen.

Erstellen eines EDM mit dem Assistenten
Um ein Entitätsmodell zu erstellen, fügen Sie zunächst Ihrem Projekt eine neue ADO.NET-Entitätsdatenmodelldatei hinzu (siehe Abbildung 3). Sobald Sie dies tun, werden Sie vom Entitätsdatenmodell-Assistenten aufgefordert, entweder ein Modell aus einer Datenbank zu generieren oder mit einem leeren Modell zu beginnen. Das Generieren des Modells aus vorhandenen Datenbanktabellen ist ein guter Ausgangspunkt, sofern Sie Zugriff auf die Datenbank haben. Bei einigen Entwicklungsmethoden wie z. B. der domänengesteuerten Entwicklung wird empfohlen, zuerst das Entitätsdomänenmodell zu entwickeln, bevor Sie die Datenbank einrichten. Wenn Sie nach dieser Methode vorgehen möchten, sollten Sie ein leeres Modell erstellen und anschließend mit dem visuellen EDM-Designer Ihre Entitäten erstellen. In meinen Beispielen habe ich ein Entitätsmodell entwickelt, indem ich die Northwind-Datenbank als Ausgangspunkt verwendet habe.
Abbildung 3 Fügen Sie Ihrem Projekt eine EDM-Datei hinzu (Klicken Sie zum Vergrößern auf das Bild)
Auf dem nächsten Bildschirm des Assistenten werden Sie dazu aufgefordert, die Datenbankverbindungsinformationen einzugeben. Dann werden Sie vom Assistenten gebeten, die Datenbankobjekte auszuwählen, die in das Modell aufgenommen werden sollen. Wie Sie in Abbildung 4 sehen können, habe ich alle in der Northwind-Datenbank enthaltenen Tabellen und gespeicherten Prozeduren außer denjenigen ausgewählt, die bei der Diagrammerstellung helfen. Die Tabellen werden zunächst durch direkte Zuordnung Entitäten zugewiesen, und die gespeicherten Prozeduren können Methoden des generierten Containers zugeordnet werden.
Abbildung 4 Wählen Sie Datenbankobjekte aus 
Sobald Sie die Datenbankobjekte angegeben haben, die Sie in das Modell aufnehmen möchten, generiert der EDM-Assistent die .edmx-Datei, die das Modell und die Zuordnungen definiert, und fügt dem Projekt die entsprechenden Verweise hinzu, die von Entity Framework benötigt werden. Die .edmx-Datei ist eine XML-Datei, die vier Hauptabschnitte enthält: Informationen zum visuellen Layout des konzeptionellen Modells im Designer, die CSDL des konzeptionellen Modells, die MSL der Zuordnungsschicht und die SSDL des physischen Modells. Alle diese Informationen sind in einer einzigen Datei enthalten.
Die Designerinformationen der .edmx-Datei helfen Visual Studio beim Anlegen des Entitätsmodells im Designer. Allerdings werden sie nur in zur Entwurfszeit verwendet. Die CSDL, die MSL und die SSDL werden beim Kompilieren dazu verwendet, Klassen zu generieren, die das EDM repräsentieren.

Gespeicherte Prozeduren im Entitätsmodell
Ich habe beschlossen, dem von mir erstellten Entitätsmodell gespeicherte Prozeduren hinzuzufügen, aber die gespeicherten Prozeduren wurden nur der SSDL-Definition hinzugefügt. Da eine gespeicherte Prozedur mit vielen verschiedenen Tabellen oder anderen Objekten einer Datenbank interagieren kann, ordnet Entity Framework die gespeicherte Prozedur nicht automatisch einer bestimmten Entität in der CSDL zu. Wenn Sie eine Methode erstellen möchten, die einer gespeicherten Prozedur zugeordnet wird, können Sie dies tun, indem Sie in der .edmx-Datei den XML-Code bearbeiten.
Um eine Methode zu erstellen, die Entitäten eines bestimmten Typs zurückgibt, wähle ich zuerst eine gespeicherte Prozedur. In diesem Beispiel füge ich eine Methode namens „GetTenMostExpensiveProducts“ hinzu, die die gespeicherte Prozedur „Ten Most Expensive Products“ (Die zehn teuersten Produkte) ausführt und Product-Objekte als Ergebnis zurückgibt. Da die SSDL die gespeicherte Prozedur bereits als Function-Element beschreibt, besteht der nächste Schritt darin, die Methode in der CSDL hinzuzufügen. Dies kann ich tun, indem ich das FunctionImport-Element als untergeordnetes Element des EntityContainer-Elements hinzufüge:
<FunctionImport Name="GetTenMostExpensiveProducts"
  EntitySet="Products"
  ReturnType="Collection(Self.Products)">
</FunctionImport>
Das Name-Attribut gibt den Namen der Methode für den Entitätscontainer an. Das EntitySet-Attribut gibt das EntitySet an, und das ReturnType-Attribut verweist auf den EntityType, der zurückgegeben wird, bzw. in diesem Fall auf die Auflistung von EntityTypes.
Der Verweis auf Self in diesem Beispiel ist ein Alias, der auf den aktuellen NWModel-Namespace verweist. Beachten Sie, dass hier sowohl NWModel als auch Self funktionieren würde.
Wenn in der Methode Parameter benötigt werden, können Sie diese mit einem <Parameter>-Tag hinzufügen. Wenn ich beispielsweise in der Methode einen Parameter namens „CategoryId“ für die gespeicherte Prozedur einfügen möchte, kann ich sie der Methode hinzufügen, indem ich im FunctionImport-Element das folgende XML-Element einfüge:
<Parameter Name="CategoryId" Type="Int32" Mode="in"/>
Da in meinem Beispiel kein derartiger Parameter vorkommt, habe ich diesen Schritt ausgelassen.
An diesem Punkt definiert die CSDL die Methode, den von der Methode zurückgegebenen Entitätstyp sowie das EntitySet, dem die zurückgegebenen Entitäten angehören, während die SSDL die gespeicherte Prozedur definiert. Jetzt muss die CSDL der SSDL zugeordnet werden, damit die konzeptionelle Methode weiß, welche gespeicherte Prozedur ausgeführt werden soll. Diese Zuordnung wird vorgenommen, indem folgendes FunctionImportMapping-Element im Abschnitt „EntityContainerMapping“ der MSL eingefügt wird:
<FunctionImportMapping 
  FunctionImportName="GetTenMostExpensiveProducts" 
  FunctionName="NWModel.Store.Ten_Most_Expensive_Products"/>
Hier verwendet das FunctionImportMapping-Element das FunctionName-Attribut, um auf den vollqualifizierten Namen des Function-Elements in der SSDL zu verweisen. Das FunctionImportName-Attribut verweist auf den Namen des FunctionImport-Elements in der CSDL.

Fenster im EDM
Nachdem das EDM entwickelt und erstellt wurde, können Sie verschiedene Fenster verwenden, um den Status des von Ihnen erstellten Entitätsmodells zu untersuchen. Das Fenster „Klassenansicht“ (siehe Abbildung 5), das nicht neu ist, ist recht nützlich, wenn Sie die Objekte untersuchen müssen, die zum Entwickeln verfügbar sind. Dieses Fenster zeigt beispielsweise meine neue GetTenMostExpensiveProducts-Methode in der NWEntities-EntityContainer-Klasse sowie alle Klassen an, die im NWModel-Namespace enthalten sind. In der CSDL wird es für jedes EntityType-Element eine Klasse geben, die das Element repräsentiert, und zusätzlich eine Klasse für das NWEntities-Hauptmodell.
Abbildung 5 EDM in der Klassenansicht 
Es gibt auch einige neue Fenster, die Ihnen Einblick in das Entitätsmodell und die zugehörigen Zuordnungen gewähren. Zu diesen Fenstern gehören der EDM-Designer, das Fenster „Entity Mapping Details“ (Entitätszuordnungsdetails) und das Fenster „Entity Model Browser“ (Entitätsmodellbrowser).
Der Entitätsmodellbrowser zeigt alle CSDL- und SSDL-Komponenten. Diese Komponenten umfassen die CSDL-Komponenten „EntityTypes“, „Associations“, „EntitySets“, „AssociationsSets“ und „Function Imports“ sowie sämtliche SSDL-Elemente.
Der Entitätsmodell-Designer (siehe Abbildung 6) zeigt ein Layout mit dem von Ihnen generierten Modell. Das Entitätsmodell kann mit diesem Designer, der alle Elemente des konzeptionellen Modells in einem visuellen Layout darstellt, angezeigt und bearbeitet werden. Beachten Sie, dass in Abbildung 6 eine Product-Entität zu sehen ist, die den Product-EntityType in der CSDL repräsentiert. Jeder EntityType enthält eine Liste von skalaren Eigenschaften und Navigationseigenschaften.
Abbildung 6 EDM-Designer (Klicken Sie zum Vergrößern auf das Bild)
Um durch Zuordnungen in der CSDL zu navigieren, werden Navigationseigenschaften verwendet. Die Navigationseigenschaften werden in ihrer EntityType-Klasse zu öffentlichen Eigenschaften und werden dazu verwendet, auf eine andere Entität oder Entitätsgruppe zu verweisen, die mit der ursprünglichen Entität verknüpft ist. So besitzt beispielsweise der Product-EntityType eine Navigationseigenschaft namens „Categories“, die auf eine Category-Entität für eine bestimmte Product-Entitätsinstanz verweist. Der Category-EntityType besitzt auch eine Navigationseigenschaft namens „Products“. Diese Eigenschaft ist vorhanden, damit Sie bei einer Category-Entitätsinstanz auf die zugehörigen Product-Entitäten verweisen können.
Mit dem Designer können Entitätstypen, Zuordnungen, skalare Eigenschaften und Navigationseigenschaften hinzugefügt, bearbeitet oder entfernt werden. Sie können beispielsweise die Namen aller EntityTypes in ein einzelnes Format ändern, das von Entwicklern beim Benennen von Entitäten gern verwendet wird. Klicken Sie einfach auf den Namen des EntityType, und bearbeiten Sie den vorgegebenen Namen. Sie können aber auch den EntityType auswählen und seinen Namen im Eigenschaftenfenster ändern. In den vorliegenden Beispielen habe ich alle EntityTypes in ein einzelnes Format umbenannt, das in Abbildung 6 zu sehen ist. Nach dieser Änderung musste ich auch den von mir geänderten XML-Code bearbeiten, um den Aufruf der gespeicherten Prozedur als Methode des NWEntities-EntityContainer hinzuzufügen. Das war recht einfach, denn ich musste lediglich den Code so aktualisieren, dass er, wie im Folgenden zu sehen ist, auf den Product-EntityType (statt auf Products) verweist:
<FunctionImport Name="GetTenMostExpensiveProducts"
  EntitySet="Products"
  ReturnType="Collection(Self.Product)">
</FunctionImport>
Damit komme ich zu einem wichtigen Punkt. Sie sollten Ihren Hauptelementen (z. B. EntityType, EntityContainer und Association) Namen geben, bevor Sie damit beginnen, das Datenmodell zu ändern. Dadurch wird die Anzahl der notwendigen manuellen Änderungen minimiert, und zugleich wird die Zahl der Änderungen gesenkt, die möglicherweise am Code vorgenommen werden müssen, den Sie bereits für das Datenmodell geschrieben haben. Falls Probleme auftreten sollten, wird Ihnen das Fenster „Error List“ (Fehlerliste) eine große Hilfe sein, denn darin wird mit hoher Wahrscheinlichkeit angezeigt, für welche Elemente im XML-Code ungültige Verweise enthalten sind.
Während der Designer das konzeptionelle Modell zeigt, können Sie über das Fenster „Entity Mapping Details“ (Entitätszuordnungsdetails) die Zuordnungen (die MSL) zwischen dem EDM und dem Datenspeicher anzeigen und bearbeiten.

Abgeleitete Entitäten
Dieselben Tools, die Ihnen helfen, Ihr Entitätsmodell zu entwickeln, können Ihnen auch helfen, das Modell zu ändern. Einer der wichtigsten Aspekte der objektorientierten Programmierung ist das Konzept der Vererbung. Das EDM unterstützt das Erstellen und Ändern vererbter Entitäten, und zwar sowohl im XML-Code als auch visuell im EDM-Designer.
Um Ihnen das Entwickeln vererbter Entitäten zu demonstrieren, werde ich basierend auf dem Product-EntityType einen EntityType namens „DiscontinuedProduct“ erstellen. Der Product-EntityType besitzt eine boolesche skalare Eigenschaft namens „Discontinued“, die ich als die Bedingung verwenden werde, die ausgewertet wird, um den spezifischen Instanztyp eines Produkts zu ermitteln. Ich verwende dazu den Entitätsmodell-Designer, klicke mit der rechten Maustaste im Designer und wähle im Kontextmenü die Option „Add“ (Hinzufügen) | „Entity“ (Entität). Danach gebe ich als neuen Entitätsnamen „DiscontinuedProduct“ ein und wähle Product als Basisentität aus (siehe Abbildung 7).
Abbildung 7 Erstellen einer vererbten Entität 
Da ich im nächsten Schritt den unterscheidenden Faktor zwischen diesen Entitäten durch eine Bedingung definieren will, wähle ich im Designer den Product-EntityType aus und wechsle zum Fenster „Entity Mapping Details“ (Entitätszuordnungsdetails). Danach wähle ich in den Spaltenzuordnungen die Discontinued-Eigenschaft aus und entferne diese Zuordnung. Dadurch wird die Discontinued-Eigenschaft sowohl aus dem Product-EntityType als auch aus dem DiscontinuedProduct-EntityType entfernt (beim Erstellen einer Bedingung dürfen die für die Bedingung verfügbaren Ausdrücke nicht bereits als Eigenschaft verwendet werden). Danach wechsle ich in den Abschnitt „Maps to Products“ (Zuordnungen zu Produkten) des Fensters „Entity Mapping Details“ (Entitätszuordnungsdetails) und füge die Bedingung „Discontinued = 0“ hinzu. Anschließend wähle ich den DiscontinuedProduct-EntityType aus und füge die Bedingung „Discontinued = 1“ hinzu.
Mit diesem Verfahren ist das Erstellen abgeleiteter Entitäten wirklich ziemlich einfach. Wenn Sie möchten, können Sie der abgeleiteten Entität neben den Eigenschaften seiner Basisklasse auch zusätzliche Eigenschaften hinzufügen. Wenn Sie in Ihrem .NET-Code eine Instanz eines DiscontinuedProduct-EntityType erstellen und speichern, erkennt Entity Framework, dass es die Spalte „Discontinued“ in der Datenbank wegen der dafür festgelegten Bedingungen auf den Wert 0 setzen muss.
Die Bedingungen werden im Abschnitt „EntityTypeMapping“ der MSL erstellt und erzwingen lediglich die Verwendung eines Filters beim Abrufen von Zeilen. Wenn Daten gespeichert werden, wird durch die Bedingungen anhand des abgeleiteten Typs der Wert ermittelt, der in die zugrunde liegende Datenbankspalte geschrieben wird.

Zusammenfassung
Mit den Entwurfstools in Visual Studio 2008 und den XML-Dateien können Sie ein Entitätsdatenmodell entwickeln, das mit Vererbung arbeitet, gespeicherte Prozeduren aufruft und Ihren Geschäft modelliert, statt Code direkt für Ihr relationales Datenbankschema zu schreiben. Sobald ein stabiles Entitätsmodell entwickelt wurde, ist es sehr einfach, über APIs wie Object Services damit zu interagieren. Außerdem erfordern Änderungen am Datenmodell keine Änderungen an der Datenbank, da die Zuordnungen das konzeptionelle Modell vom Speichermodell isolieren können. Wenn Sie mit einigen der hier im Zusammenhang mit Entity Framework behandelten Konzepte nicht vertraut sind, empfehle ich Ihnen sehr, meine Übersicht über Entity Framework zu lesen, die unter msdn.microsoft.com/msdnmag/issues/07/07/DataPoints verfügbar ist.

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) und ein Baseballfan, der in den meisten Sommernächten mit seiner Familie und seinem treuen Hund Kadi die Yankees anfeuert. John Papa ist C#-MVP und hat mehrere Bücher über ADO, XML und SQL Server verfasst. Er hält häufig Vorträge auf Branchenkonferenzen wie VSLive und führt einen Blog unter codebetter.com/blogs/john.papa.

Page view tracker