Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 6. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Das Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Strukturen und Enumerationen
7 Fehlerbehandlung und Debugging
8 Auflistungsklassen (Collections)
9 Generics – Generische Datentypen
10 Weitere C#-Sprachfeatures
11 LINQ
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 XML
15 Multithreading und die Task Parallel Library (TPL)
16 Einige wichtige .NET-Klassen
17 Projektmanagement und Visual Studio 2012
18 Einführung in die WPF und XAML
19 WPF-Layout-Container
20 Fenster in der WPF
21 WPF-Steuerelemente
22 Elementbindungen
23 Konzepte von WPF
24 Datenbindung
25 Weitere Möglichkeiten der Datenbindung
26 Dependency Properties
27 Ereignisse in der WPF
28 WPF-Commands
29 Benutzerdefinierte Controls
30 2D-Grafik
31 ADO.NET – Verbindungsorientierte Objekte
32 ADO.NET – Das Command-Objekt
33 ADO.NET – Der SqlDataAdapter
34 ADO.NET – Daten im lokalen Speicher
35 ADO.NET – Aktualisieren der Datenbank
36 Stark typisierte DataSets
37 Einführung in das ADO.NET Entity Framework
38 Datenabfragen des Entity Data Models (EDM)
39 Entitätsaktualisierung und Zustandsverwaltung
40 Konflikte behandeln
41 Plain Old CLR Objects (POCOs)
Stichwort

Download:
- Beispiele, ca. 62,4 MB

Buch bestellen
Ihre Meinung?

Spacer
Visual C# 2012 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2012

Visual C# 2012
Rheinwerk Computing
1402 S., 6., aktualisierte und erweiterte Auflage 2013, geb., mit DVD
49,90 Euro, ISBN 978-3-8362-1997-6
Pfeil 41 Plain Old CLR Objects (POCOs)
Pfeil 41.1 Ein erstes Projekt mit POCO-Klassen
Pfeil 41.1.1 Erstellen einfacher POCO-Klassen
Pfeil 41.1.2 Erstellen des Objektkontextes
Pfeil 41.2 Datenabfrage mit Hilfe der POCOs
Pfeil 41.2.1 In Beziehung stehende Daten laden
Pfeil 41.3 Änderungen verfolgen
Pfeil 41.3.1 Die Methode »DetectChanges«
Pfeil 41.3.2 In Beziehung stehende POCOs aktualisieren

Galileo Computing - Zum Seitenanfang

41.3 Änderungen verfolgenZur nächsten Überschrift


Galileo Computing - Zum Seitenanfang

41.3.1 Die Methode »DetectChanges«Zur nächsten ÜberschriftZur vorigen Überschrift

Verwenden wir POCO-Klassen in unserer Anwendung, liefert uns das Abfrageergebnis abhängig von der Abfrage mehr oder weniger viele POCO-Objekte. Für jedes dieser Objekte erzeugt der Objektkontext ein ObjectStateEntry-Objekt. Das Verhalten unterscheidet sich also nicht von dem, wenn wir Entitäten verwenden, die von EntityObject abgeleitet sind. Trotzdem gibt es einen wichtigen Unterschied: EntityObject-basierte Objekte kommunizieren mit dem Objektkontext und teilen ihm automatisch mit, wenn sich eine Eigenschaft verändert hat. Der Objektkontext ist daher in der Lage, den Zustand der Objekte zu verfolgen und das zugehörige ObjectStateEntry-Objekt mit dem Objekt zu synchronisieren.

POCO-Objekte hingegen leiten nicht die Klasse EntityObject ab und sind auch nicht in der Lage, dem Objektkontext Informationen über Änderungen mitzuteilen. Daher ist die Zustandsverfolgung seitens des Objektkontextes zunächst einmal nicht möglich. Besondere Umstände verlangen besondere Maßnahmen. Es muss daher eine andere Möglichkeit geben, die es dem Objektkontext ermöglicht, die Daten eines POCO-Objekts mit denen des entsprechenden ObjectStateEntry-Objekts zu synchronisieren. Genau diese Aufgabe übernimmt die Methode DetectChanges des Objektkontextes. Die Arbeitsweise von DetectChanges zeigt das folgende Listing.

using (NorthwindContext context = new NorthwindContext())
{
Product p = (from prod in context.Products
where prod.CategoryID == 1
select prod).First();
ObjectStateEntry ose = context.ObjectStateManager.GetObjectStateEntry(p);
Console.WriteLine(ose.State);

// Änderung einer Eigenschaft

p.ProductName = "Möhren";
Console.WriteLine(ose.State);

// Änderung synchronisieren

context.DetectChanges();
Console.WriteLine(ose.State);
}

Listing 41.7 Einsatz der Methode »DetectChanges«

Wir fragen zuerst eine Datenzeile ab und lassen uns deren Zustand über das zugeordnete ObjectStateEntry-Objekt ausgeben. Er ist Unchanged. Danach ändern wir eine Eigenschaft und lassen uns erneut den Zustand der Entität ausgeben. Er ist immer noch Unchanged, obwohl sich offensichtlich eine Eigenschaft geändert hat. Das ObjectStateEntry-Objekt spiegelt in diesem Moment noch nicht den tatsächlichen Zustand der Entität wider.

Im dritten Schritt rufen wir die Methode DetectChanges auf. Erst danach wird das ObjectStateEntry-Objekt der geänderten POCO-Entität als Modified gekennzeichnet. Somit ist bewiesen, dass die Methode DetectChanges in der Lage ist, das POCO-Objekt mit seinem zugeordneten ObjectStateEntry zu synchronisieren.

Natürlich werden Sie die Änderung auch in die Datenbank schreiben wollen. Wie Sie sich erinnern, ist dafür die Methode SaveChanges des Objektkontextes zuständig. Sie müssen jetzt nicht glauben, dass aufgrund der zuvor gemachten Ausführungen die Methode DetectChanges vor dem SaveChanges-Aufruf ausgeführt werden muss, denn das geschieht implizit in der Methode SaveChanges. Sie können sich davon überzeugen, wenn Sie das folgende Listing testen.

using(NorthwindContext context = new NorthwindContext())
{
Product product = (from prod in context.Products
where prod.CategoryID == 1
select prod).First();

// Änderung einer Eigenschaft

product.ProductName = "Möhren";
context.SaveChanges();
}

Listing 41.8 Speichern einer geänderten POCO-Entität

Die Änderung wird tatsächlich in die Tabelle Products der Datenbank Northwind geschrieben, wie das Öffnen der Tabelle beispielsweise im SQL Server Management Studio beweist.

Sie finden das gesamte Projekt auf der Buch-DVD unter ...\Beispiele\Kapitel 41\POCO_ Sample2.


Galileo Computing - Zum Seitenanfang

41.3.2 In Beziehung stehende POCOs aktualisierenZur nächsten ÜberschriftZur vorigen Überschrift

In einem Entity Data Modell, das auf Entitäten basiert, die von EntityObject abgeleitet sind, werden Änderungen auf beiden Seiten einer Beziehung automatisch verfolgt. Nehmen wir an, wir würden ein neues Produkt erstellen und es der EntityCollection der Kategorie zuordnen, zu der das neue Produkt gehört. Automatisch wird dann auch die EntityReference des neuen Produkts mit der entsprechenden Kategorie verknüpft.

Im folgenden Listing wird das demonstriert. Zur Laufzeit wird an der Konsole Beverages ausgegeben, also die Kategorie, zu der das Produkt gehört. Beachten Sie, dass in diesem Beispiel EntityObject-basierte Entitäten benutzt werden und keine POCOs.

using (NorthwindEntities context = new NorthwindEntities())
{
var cat = (from c in context.Categories.Include("Products")
select c).First(c => c.CategoryID == 1);
Product prod = new Product { ProductName = "Gurke",
Discontinued = false };
cat.Products.Add(prod);
Console.WriteLine(prod.Category.CategoryName);
}

Listing 41.9 Aktualisierung in Beziehung stehender Entitäten (keine POCOs)

Lassen Sie uns nun denselben Code in einem Entity Data Model ausführen, in dem die Entitäten durch POCOs beschrieben werden. Sie werden feststellen, dass in der Zeile der Konsolenausgabe eine Ausnahme vom Typ NullReferenceException ausgelöst wird, weil die Eigenschaft Category des neuen Produkts den Wert null aufweist. Das ist auch weiter nicht verwunderlich, da in den POCO-Klassen keine automatische Unterstützung der Beziehung codiert ist, die dafür sorgt, dass die Eigenschaft Category auf eine gültige Kategorie verweist – nämlich die, zu der das neue Produkt gehört.

Zur Lösung unseres Problems bieten sich drei Ansätze an:

  • die Methode DetectChanges
  • die Bereitstellung spezifischer Methoden in den POCO-Klassen
  • Proxy-Objekte für POCO-Entitäten

Wir wollen uns diese drei Ansätze etwas genauer ansehen.

Variante 1: Die Methode »DetectChanges«

Die einfachste Lösung ist sicherlich die des Aufrufs der Methode DetectChanges, auf die ich weiter oben schon eingegangen bin. Bezogen auf das Beispiel im vorangehenden Listing müssen wir DetectChanges aufrufen, nachdem das neue Produkt der Liste aller Produkte hinzugefügt worden ist.

using (NorthwindContext context = new NorthwindContext())
{
...
cat.Products.Add(prod);
context.DetectChanges();
Console.WriteLine(prod.Category.CategoryName);
}

Listing 41.10 Objektkontext und POCO-Entität synchronisieren

Die Methode DetectChanges zwingt den Objektkontext dazu, die ObjectStateEntry-Objekte, die bekanntlich der Zustandsverfolgung dienen, zu aktualisieren. Gewissermaßen besorgt sich der Objektkontext damit einen Schnappschuss des aktuellen Zustands der skalaren Eigenschaften einer POCO-Entität. Darüber hinaus sorgt die Methode auch dafür, dass die gegenseitige Abhängigkeit der beteiligten POCO-Entitäten berücksichtigt wird – in unserem Beispiel also die der Category- und Product-Entität.

Es scheint verlockend, sich immer für diesen einfachen Ansatz zu entscheiden. Jedoch sollten Sie dabei immer im Auge behalten, dass die Operation von DetectChanges auf jede im Objektkontext befindliche Entität ausgeführt wird.

Variante 2: Spezifische Methoden in den POCO-Klassen

Einen besseren Ansatz verfolgt die folgende Variante. Dabei werden in den beiden POCO-Klassen Ergänzungen vorgenommen, die die Beziehung zwischen Category und Product hinsichtlich der gegenseitigen Abhängigkeit widerspiegeln.

Sehen wir uns zuerst die Änderung in der Klasse Category an. Hier wird eine benutzerdefinierte Methode bereitgestellt, die zwei Aufgaben bewerkstelligen muss:

  • Sie muss das neue Produkt zur Liste aller Produkte der betreffenden Kategorie hinzufügen.
  • Sie muss dem neuen Produkt mitteilen, zu welcher Kategorie es gehört.

In Listing 41.11 heißt die Methode AddProduct, kann aber auch beliebig anders benannt werden. Als Argument erwartet sie die Referenz auf das neue Produkt.

public class Category {
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public byte[] Picture { get; set; }
public virtual ICollection<Product> Products { get; set; }

public void AddProduct(Product prod)
{
if (Products == null) Products = new List<Product>();
if (!Products.Contains(prod)) Products.Add(prod);
if (prod.Category != this) prod.Category = this;
}

}

Listing 41.11 Anpassung der POCO-Klasse »Category«

Wichtig ist in der Methode AddProduct die letzte Prüfung, bei der untersucht wird, ob das neue Produkt bereits eine Referenz auf die entsprechende Kategorie hat. Vergessen Sie die Prüfung, befinden Sie sich (unter Berücksichtigung der noch zu ändernden Product-Klasse) in einer Endlosschleife.

In der POCO-Klasse Product müssen wir die automatische Eigenschaft Category auflösen, weil im set-Zweig eine zusätzliche Anweisung notwendig ist. In dieser rufen wir die AddProduct-Methode der Category-Referenz auf und übergeben ihr das neue Produkt.

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; }
private Category _Category;

public virtual Category Category
{
get { return _Category; }
set {
_Category = value;
_Category.AddProduct(this);
}
}

}

Listing 41.12 Anpassung der POCO-Klasse »Product«

Die Methode AddProduct muss in unserer Testanwendung natürlich berücksichtigt werden, wenn wir ein neues Produkt hinzufügen wollen.

using(NorthwindContext context = new NorthwindContext())
{
var cat = (from c in context.Categories.Include("Products")
select c).First(c => c.CategoryID == 1);
Product prod = new Product { ProductName = "Gurke",
Discontinued = false };
cat.AddProduct(prod);
Console.WriteLine(prod.Category.CategoryName);
}

Listing 41.13 Testen der POCO-Klassen der Listings 41.10 und 41.11

Sie finden das gesamte Projekt dieses Abschnitts auf der Buch-DVD unter ...\Beispiele\Kapitel 41\POCO_Sample3.

Variante 3: Proxy-Objekte für POCO-Entitäten

Auf sehr einfache Weise können Sie POCO-Objekte erstellen, die sich nicht von den üblichen EntityObject-Objekten unterscheiden. Dazu müssen Sie nur in der POCO-Klasse ausnahmslos jede Eigenschaft virtual kennzeichnen. Erfüllen Ihre POCO-Klassen diese Bedingung, wird das Entity Framework um die POCO-Klassen automatisch einen Wrapper vom Typ DynamicProxy erzeugen, der von der POCO-Klasse abgeleitet ist. Die Proxy-Klasse ist in der Lage, zur Laufzeit zahlreiche Features zur Verfügung zu stellen, die von EntityObject her bekannt sind. Dazu gehört neben dem Lazy Loading auch die für uns so wichtige Zustandsverwaltung. Der aktuelle Zustand kann mit der Ergänzung sofort über das ObjectStateEntry-Objekt abgefragt werden, und der Aufruf der Methode DetectChanges erübrigt sich.

public class Category {
public virtual int CategoryID { get; set; }
public virtual string CategoryName { get; set; }
public virtual string Description { get; set; }
public virtual byte[] Picture { get; set; }
public virtual ICollection<Product> Products { get; set; }
}

public class Product {
public virtual int ProductID { get; set; }
public virtual string ProductName { get; set; }
public virtual int SupplierID { get; set; }
public virtual string QuantityPerUnit { get; set; }
public virtual decimal UnitPrice { get; set; }
public virtual short UnitsInStock { get; set; }
public virtual short UnitsOnOrder { get; set; }
public virtual short ReorderLevel { get; set; }
public virtual bool Discontinued { get; set; }
public virtual int CategoryID { get; set; }
public virtual Category Category {get; set;}
}

Listing 41.14 Vorbereitung der POCO-Klassen zur Erstellung von Proxy-Objekten

Mit diesen Änderungen wird der Beispielcode aus Listing 41.6 auch dann einwandfrei funktionieren, wenn wir auf den Aufruf von DetectChanges verzichten.

using (NorthwindContext context = new NorthwindContext())
{
var cat = (from c in context.Categories.Include("Products")
select c).First(c => c.CategoryID == 1);
Product prod = new Product { ProductName = "Gurke",
Discontinued = false };
cat.Products.Add(prod);
Console.WriteLine(prod.Category.CategoryName);
}

Listing 41.15 Testen der Proxy-Objekte

Sie finden das gesamte Projekt dieses Abschnitts auf der Buch-DVD unter ...\Beispiele\Kapitel 41\POCO_Sample4.

Interessant ist es, wenn wir die Proxy-Klasse etwas genauer unter die Lupe nehmen. Dazu legen wir einen Haltepunkt in der Anweisung fest, in der die Konsolenausgabe codiert ist. Abbildung 41.2 zeigt den für uns wesentlichen Ausschnitt im Lokal-Fenster.

Abbildung

Abbildung 41.2 POCO-Objekt im Debugger

Wir müssen feststellen, dass es sich bei dem Objekt cat nicht einfach nur um ein Objekt vom Typ Category handelt. Tatsächlich wird der Typ des Objekts durch eine Kombination aus dem tatsächlichen Typ und einem Hash gebildet. Die Navigationseigenschaft Products hingegen wird auf den Typ EntityCollection<T> abgebildet.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2012

Visual C# 2012
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Rheinwerk Verlag GmbH 2013
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de