41 Plain Old CLR Objects (POCOs)
Alle Entitätsklassen, die wir bisher behandelt haben, waren von EntityObject abgeleitet. Natürlich ziehen wir zahlreiche Vorteile aus dieser Tatsache, beispielsweise die Zustandsverfolgung im Zusammenspiel mit dem Objektkontext. Damit lassen sich zweifelsfrei viele Szenarien abdecken, aber eben nur viele und nicht alle. Stellen Sie sich beispielsweise vor, Sie möchten Entitätsobjekte über einen Webservice Clients zur Verfügung stellen, die nicht auf .NET basieren. Das ist mit Objekten, die von EntityObject abgeleitet sind, nicht möglich.
Einen Ausweg aus diesem Dilemma bieten Plain Old CLR Objects, kurz POCOs oder auch Datenklassen genannt. POCOs weisen keine bestimmte Basisklasse auf und implementieren auch keine Interfaces. Sie beschreiben im Grunde genommen nur die Eigenschaften, die das Entity Data Model den Entitäten vorschreibt. Damit koppeln sich POCOs komplett von der Infrastruktur des Entity Frameworks ab und werden von diesem vollkommen unabhängig. Diese strikte Trennung führt in Konsequenz zur Unabhängigkeit der Datenklassen von den Anforderungen des Entity Frameworks oder auch einzig und allein nur zu besser wartbarem Code.
41.1 Ein erstes Projekt mit POCO-Klassen

41.1.1 Erstellen einfacher POCO-Klassen

POCO sind Datenklassen, die nicht von EntityObject abgeleitet sind. Weil die Zustandsverfolgung und die Persistenz im Entity Framework auf der Kopplung zwischen EntityObject und ObjectContext basieren, sind die Datenklassen zunächst einmal verwaist. Nichtsdestotrotz, ein »Umfeld«, in dem die POCOs nicht nur materialisiert, sondern zu einem späteren Zeitpunkt auch in die Datenbank zurückgeschrieben werden können, muss dennoch bereitgestellt werden. Dazu ist ein Entity Data Model notwendig, in dem die Entitäten im konzeptionellen Modell zwar beschrieben, aber nicht – wie bisher – erzeugt werden.
Die Vorgehensweise sehen wir uns am besten in einem neuen Projekt an. Dabei sollen die POCO-Klassen in einer Klassenbibliothek codiert werden, das Entity Data Model samt dem Testcode in einer Konsolenanwendung.
Zuerst legen wir ein neues Projekt vom Typ einer Konsolenanwendung an. Im ersten Schritt erstellen wir das EDM auf dieselbe Weise wie in allen anderen Beispielen zuvor. Das EDM soll auch in diesem Projekt die beiden Entitäten Product und Category beschreiben. Wenn das erledigt ist, folgt ein ganz wichtiger Schritt. Da wir POCO-Klassen bereitstellen wollen, können wir auf das automatische Erzeugen der beiden Entitäten und der ObjectContext-Klasse verzichten. Wir erreichen das, indem wir uns im Eigenschaftsfenster von Visual Studio die Eigenschaften des EDM anzeigen lassen und hier die Eigenschaft Codegenerierungsstrategie auf Keine einstellen (siehe Abbildung 41.1). Der Vorgabewert dieser Eigenschaft lautet Standard.
Abbildung 41.1 Unterbinden der automatischen Codeerzeugung
Mit dieser Einstellung erreichen wir, dass zwar weiterhin die Schemadateien SSDL, CSDL und MSL erstellt werden, jedoch bleibt die Datei Northwind.Designer.cs leer. Gewissermaßen zwingen wir uns mit diesem Schritt, entsprechende POCO-Klassen zu codieren und darüber hinaus eine eigene ObjectContext-Klasse zu schreiben.
Lassen Sie uns als Nächstes die POCO-Klassen erstellen. Um die strikte Trennung der Datenklassen und der Clientanwendung zu verdeutlichen, fügen wir der Projektmappe ein neues Projekt vom Typ Klassenbibliothek hinzu. Wir geben dem neuen Projekt den Bezeichner NorthwindPOCOS. Anschließend erstellen wir zwei Klassen und geben diesen die Namen Product und Category. Hierbei handelt es sich um das Grundgerüst der beiden POCO-Klassen.
Da POCO-Klassen mit dem Entity Framework zusammenarbeiten sollen, gibt es ein paar Regeln, die es zu beachten gilt:
- POCO-Klassen müssen public sein und dürfen nicht mit sealed oder abstract gekennzeichnet werden.
- Die Bezeichner der POCO-Klassen müssen mit der entsprechenden Vorgabe im Entity Data Model übereinstimmen.
- POCO-Klassen müssen einen parameterlosen Konstruktor haben.
- Jede Eigenschaft, die im konzeptionellen Modell beschrieben ist, muss eine gleichnamige Entsprechung in der POCO-Klasse haben. Das gilt auch für die Navigationseigenschaften.
- Die Navigationseigenschaften, die eine Menge repräsentieren, müssen durch einen Datentyp beschrieben werden, der ICollection<T> implementiert.
Mit diesen Vorgaben sehen die beiden POCO-Klassen wie in Listing 41.1 gezeigt aus.
public class Category {
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public byte[] Picture { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product {
public int ProductID { get; set; }
public string ProductName { get; set; }
public int SupplierID { get; set; }
public string QuantityPerUnit { get; set; }
public decimal UnitPrice { get; set; }
public short UnitsInStock { get; set; }
public short UnitsOnOrder { get; set; }
public short ReorderLevel { get; set; }
public bool Discontinued { get; set; }
public int CategoryID { get; set; }
public Category Category { get; set; }
}
Listing 41.1 Die beiden POCO-Klassen »Product« und »Category«
Da die Klassen- und Eigenschaftsbezeichner denen der Entitäten und deren Eigenschaften entsprechen, sind unsere POCOs darauf vorbereitet, mit dem Entity Framework zu interagieren, ohne gleichzeitig von diesem abhängig zu sein. An dieser Stelle sei noch einmal ausdrücklich darauf hingewiesen, dass POCO-Klassen nicht von der Basis EntityObject abgeleitet sind und damit auch keinerlei Möglichkeit haben, mit dem Objektkontext, den wir im nächsten Schritt noch erstellen müssen, zu kommunizieren.
Im weiteren Verlauf dieses Kapitels werden Sie erfahren, dass an POCO-Klassen manchmal noch weitere Anforderungen gestellt werden, um die Zustandsverwaltung des Objektkontextes zu unterstützen. Für den Anfang und die ersten Experimente soll aber der bisherige Klassencode ausreichend sein.
41.1.2 Erstellen des Objektkontextes
Wollen wir POCOs, oder allgemein gesagt Datenklassen, zur Datenbearbeitung mit dem Entity Framework nutzen, benötigen wir in jedem Fall einen Objektkontext, der die Schaltzentrale des Entity Frameworks darstellt: Er ermöglicht es, Abfragen abzusetzen, Entitäten zu materialisieren, den Zustand der Entitäten zu verfolgen und Änderungen in die Datenbank zu schreiben.
Da wir die automatische Codeerzeugung beim Erstellen des Entity Data Models unterdrückt haben, müssen wir die erforderliche Klasse selbst schreiben, die natürlich von ObjectContext abgeleitet werden muss. Um die strikte Trennung zu den vom Entity Framework unabhängigen POCO-Klassen nicht aufzubrechen, implementieren wir den Objektkontext sinnvollerweise in der Konsolenanwendung.
public class NorthwindContext : ObjectContext
{
// Felder
private ObjectSet<Category> _categories;
private ObjectSet<Product> _products;
// Konstruktor
public NorthwindContext()
: base("name=NorthwindEntities", "NorthwindEntities")
{
_categories = CreateObjectSet<Category>();
_products = CreateObjectSet<Product>();
}
// Eigenschaften
public ObjectSet<Category> Categories
{
get { return _categories; }
}
public ObjectSet<Product> Products
{
get { return _products; }
}
}
Listing 41.2 Die Klasse, die den Objektkontext beschreibt
Der Konstruktor leitet den Aufruf an den Konstruktor der Basisklasse um. Dabei wird neben der in der Datei App.config definierten Verbindungszeichenfolge auch der Bezeichner des Entitätscontainers übergeben. Die Menge aller Entitäten des EDM wird durch die schreibgeschützten Eigenschaften Categories und Products beschrieben, die beide vom Typ ObjectSet<T> sind. Initialisiert werden die beiden Eigenschaften im Konstruktor.
Damit wir in der Konsolenanwendung auf die POCO-Klassen zugreifen können, muss in der Konsolenanwendung ein Verweis auf die Klassenbibliothek gelegt werden.
Damit hätten wir bereits alle notwendigen Arbeiten erledigt: Wir haben die POCO-Klassen, die als vom Entity Framework unabhängige Komponenten die abgefragten Daten aufnehmen können, und wir haben einen Objektkontext bereitgestellt, der als Bindeglied zwischen den POCOs und dem Entity Framework agiert.
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.