37.5 Die Klassen des Entity Data Models (EDM)
Wenn Sie mit dem Assistenten ein Entity Data Model bereitstellen, wird neben der EDMX-Datei auch noch eine weitere Datei mit der Dateierweiterung designer.cs erzeugt. In dieser Datei sind die Klassen enthalten, die die konzeptionelle Schicht in die Programmiersprache Ihrer Wahl umsetzen. Mit von der Partie sind die Klassen, die die Entitäten des Entity Data Models beschreiben. Bezogen auf das von uns erstellte EDM handelt es sich um die Klassen Product und Category. Außerdem wird in der Codedatei noch eine Klasse namens NorthwindEntities beschrieben. Diese stellt die Umgebung bereit, innerhalb deren die Entitätsdaten abgefragt und bearbeitet werden können.
37.5.1 Die Entitätsklassen
Im Entity Framework arbeiten Sie nicht direkt mit Datensätzen. Stattdessen werden die von der Datenbank bezogenen Daten eines Datensatzes in einem Entitätsobjekt materialisiert. Die entsprechende Entitätsklasse mit allen erforderlichen Membern beschreibt das Entity Data Model in der Datei mit der Extension designer.cs. Wir wollen uns nun die Entität Product ansehen, die die Tabelle Products der Northwind-Datenbank abbildet. Dabei sei auch in diesem Beispiel davon ausgegangen, dass im EDM neben der Entität auch die Entität Category beschrieben wird. Jede Entitätsklasse wird von der Basis EntityObject abgeleitet:
[EdmEntityType(NamespaceName="NorthwindModel", Name="Product")]
[Serializable()]
[DataContract(IsReference=true)]
public partial class Product : EntityObject
Damit hat eine Entität von Anfang an eine gewisse Basisfunktionalität. Da ein Objekt vom Typ einer Entität häufig auch die Prozessgrenzen verlassen muss (beispielsweise bei einer Webanwendung), ist die Serialisierbarkeit sehr wichtig. Aus diesem Grund ist jede Entitätsklasse mit den Attributen Serializable und DataContract verknüpft.
Zu den von der Basis EntityObject geerbten Membern gehören die beiden Eigenschaften EntityState und EntityKey. Beide spielen im Zusammenhang mit der Zustandsverfolgung und der Aktualisierung einer Entität eine wichtige Rolle. Darüber hinaus erbt eine Entität auch die beiden Ereignisse PropertyChanging und PropertyChanged, die über die Methoden OnPropertyChanging und OnPropertyChanged ausgelöst werden können.
Der Code einer Entität
Jede Entität, die auch gleichzeitig in Beziehung zu einer anderen im Entity Data Model steht, wird durch drei Methodengruppen beschrieben:
- eine Factory-Methode
- primitive Eigenschaften
- Navigationsmethoden
Betrachten wir zuerst die Factory-Methode ganz am Anfang der Entitätsklasse.
// Factory-Methode
public static Product CreateProduct(Int32 productID, String productName,
Boolean discontinued) {
Product product = new Product();
product.ProductID = productID;
product.ProductName = productName;
product.Discontinued = discontinued;
return product;
}
Grundsätzlich können Sie natürlich eine neue Entität durch den Aufruf des Konstruktors erzeugen. Das ist beispielsweise dann von Interesse, wenn Sie die neue Entität in die Datenbank als neuen Datensatz zurückschreiben wollen. Da es aber keinen parametrisierten Konstruktor gibt, bleibt Ihnen nichts anderes übrig, als allen Feldern, die nicht null sein dürfen, ausdrücklich einen Wert zu übergeben. Die Factory-Methode in der Entitätsklasse unterstützt Sie dabei in der Weise, dass für jedes nicht-null-fähige Feld ein Parameter definiert ist, dem Sie beim Methodenaufruf ein Argument übergeben müssen.
Jede Spalte in der Datenbank wird in der Entitätsklasse durch eine Eigenschaft abgebildet, die den zugehörigen Wert in einem privaten Feld speichert. Exemplarisch sei an dieser Stelle die Eigenschaft ProductName der Entität Product gezeigt.
// Primitive Eigenschaften
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public String ProductName
{
get { return _ProductName; }
set
{
OnProductNameChanging(value);
ReportPropertyChanging("ProductName");
_ProductName = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("ProductName");
OnProductNameChanged();
}
}
private String _ProductName;
partial void OnProductNameChanging(String value);
partial void OnProductNameChanged();
Entitätseigenschaften sind mit den Attributen EdmScalarProperty und DataMember verknüpft. Mit dem DataMember-Attribut werden Eigenschaften gekennzeichnet, die beim Aufruf eines Services (z. B. beim Einsatz innerhalb der WCF – Windows Communication Foundation) serialisiert werden müssen. Interessanter ist momentan das Attribut EdmScalarProperty. Dessen Eigenschaft EntityKeyProperty gibt an, ob die mit diesem Attribut verknüpfte Entitätseigenschaft Teil des Entitätsschlüssels (der in der Regel dem Primärschlüssel entspricht) ist. Wie wir wissen, trifft das nicht auf ProductName zu. Die IsNullable-Eigenschaft wiederum gibt an, ob die Entitätseigenschaft den Wert null aufweisen kann. Das trifft auf ProductName nicht zu.
Über den get-Zweig der Eigenschaft muss eigentlich kein weiteres Wort verloren werden. Er liefert ausschließlich den Wert der Eigenschaft zurück. Etwas aufwendiger ist da schon der set-Zweig implementiert. Inmitten der insgesamt fünf Anweisungen finden wir die Zuweisung des Wertes an das private Feld _ProductName. Davor wird das Ereignis ReportPropertyChanging ausgelöst und die partielle Methode OnProductNameChanging aufgerufen. Ähnliches spielt sich auch nach der Wertzuweisung ab: die Auslösung des Ereignisses ReportPropertyChanged und der Aufruf einer partiellen Methode OnProductNameChanged.
Wir sollten zuerst über die beiden Ereignisse sprechen. Die Entitätsklasse kann natürlich von mehreren Methoden in der Anwendung genutzt werden. Es ist durchaus vorstellbar, dass im Zusammenhang mit der Zuweisung an die Eigenschaft ProductName innerhalb der Methoden unterschiedliche Reaktionen verbunden werden sollen. Vielleicht soll beim Aufruf einer Methode vor der Eigenschaftswertänderung die Aktion in ein Protokoll geschrieben werden, während eine andere Methode nur eine Information an den Anwender sendet. In solchen Fällen, in denen es zu unterschiedlichen Reaktionen kommen soll, bieten sich Ereignisse an – in unserem Fall speziell die Events ReportPropertyChanging und ReportPropertyChanged.
Einen anderen Zweck verfolgt die Bereitstellung der beiden partiellen Methoden OnProductNameChanging und OnProductNameChanged. Diese dienen dazu, Code bereitzustellen, der immer dann ausgeführt wird, wenn sich die Eigenschaft ändert. Im ersten Moment scheint nichts dagegen zu sprechen, den gewünschten Code direkt in den set-Zweig der Eigenschaft zu schreiben. Es gibt aber dennoch ein Gegenargument: Wird das Entity Data Model aus der Datenbank aktualisiert, geht der zusätzliche benutzerdefinierte Code verloren. Er wird schlicht und ergreifend »aktualisiert«. Diese unerwünschte »Aktualisierung« wird mit partiellen Methoden vermieden. Partielle Methoden werden in einer partiellen Teildefinition der Klasse implementiert. Bei einer gewollten Aktualisierung des Entity Data Models bleibt der partielle Teil mit der Implementierung der partiellen Methoden davon unbetroffen.
Zum Schluss unserer Betrachtungen sollen auch die Navigationsmethoden noch erwähnt werden. In der Entität Product lauten sie Category und CategoryReference. Der Einfachheit halber sei an dieser Stelle nur die Struktur wiedergegeben:
public Category Category
{
[...]
}
public EntityReference<Category> CategoryReference
{
[...]
}
Die Navigationsmethoden dienen dazu, eine bestehende Beziehung zu nutzen, um von einer Entität zu einer in Beziehung stehenden Entität zu navigieren. In den beiden folgenden Kapiteln werden Sie den Einsatz der Navigationsmethoden erfahren.
37.5.2 Der ObjectContext
Neben den im Abschnitt zuvor beschriebenen Entitäten ist der Objektkontext das zentrale Element des Entity Frameworks. Der Objektkontext wird durch ein Objekt beschrieben, das von der Basis ObjectContext abgeleitet ist. Ohne den Objektkontext, der gleichzeitig auch der Cache für die Entitätsobjekte ist, können Sie keine Abfragen ausführen oder Objektänderungen speichern. Darüber hinaus überwacht der Objektkontext die Änderungen an den Entitätsobjekten und berücksichtigt dabei auch die Einhaltung der im EDM enthaltenen Assoziationen.
Sehen wir uns nun die elementare Struktur der Klassendefinition des Objektkontextes in unserem EDM an (Abbildung 37.12).
Abbildung 37.12 Die Klassendefinition des ObjectContexts
Der Objektkontext definiert mehrere Konstruktoren.
public NorthwindEntities() : base("name=NorthwindEntities",
"NorthwindEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
public NorthwindEntities(string connectionString) : base(connectionString,
"NorthwindEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
public NorthwindEntities(EntityConnection connection) : base(connection,
"NorthwindEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
Der parameterlose Konstruktor verwendet die in der Konfigurationsdatei (app.config) hinterlegte Verbindungszeichenfolge zum Aufbau der Verbindung zur Datenbank. Darüber hinaus haben Sie die Möglichkeit, eine Verbindungszeichenfolge explizit anzugeben oder ein EntityConnection-Objekt zu übergeben.
In den Konstruktoren wird die Methode OnContextCreated aufgerufen. Hierbei handelt es sich um eine partielle Methode, die es Ihnen ermöglicht, bei der Instanziierung der ObjectContext-Klasse eigenen Code hinzuzufügen.
Für jede im Entity Data Model beschriebene Entität wird durch den Objektkontext eine schreibgeschützte Eigenschaft bereitgestellt, die die Menge aller Objekte des entsprechenden Entitätstyps beschreibt. In unserem EDM-Beispiel handelt es sich um die Eigenschaften Products und Categories. Der Rückgabewert ist jeweils vom Typ ObjectSet<T>.
private ObjectSet<Category> _Categories;
private ObjectSet<Product> _Products;
public ObjectSet<Category> Categories {
get {
if ((_Categories == null)) {
_Categories = base.CreateObjectSet<Category>("Categories");
}
return _Categories;
}
}
public ObjectSet<Product> Products {
get {
if ((_Products == null)) {
_Products = base.CreateObjectSet<Product>("Products");
}
return _Products;
}
}
Wenn wir im nächsten Kapitel unsere LINQ-Abfragen codieren, ist die Menge der Entitäten immer Ausgangspunkt aller weiteren Überlegungen.
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.