39.3 Das »ObjectStateEntry«-Objekt
Sobald der Objektkontext eine Entität erzeugt und die Zustandsverwaltung nicht deaktiviert ist, stellt der Objektkontext zu jeder Entität auch noch ein Objekt vom Typ ObjectStateEntry bereit. Wir können also sagen, dass jede Materialisierung mit Zustandsverfolgung im Grunde genommen zwei Objekte zur Folge hat:
- das Entitätsobjekt
- das dazu gehörende ObjectStateEntry-Objekt
Das ObjectStateEntry-Objekt ist für die Zustandsverfolgung der zugeordneten Entität zuständig und stellt dafür entsprechende Eigenschaften und Methoden bereit, während die Entität im Grunde genommen nur die aus der Datenquelle bezogenen Daten kapselt. Da der Zustand einer Entität nicht unbedingt verfolgt werden muss, ist die Trennung zwischen Entität und ObjectStateEntry auch durchaus sinnvoll.
Sie erhalten die Referenz auf ein ObjectStateEntry-Objekt einer bestimmten Entität, wenn Sie die Methode GetObjectStateEntry des ObjectStateManagers unter Übergabe der Referenz der Entität aufrufen, z. B.:
ObjectStateManager osm = context.ObjectStateManager;
ObjectStateEntry entry = osm.GetObjectStateEntry(prod);
In diesem Codefragment repräsentiert die Variable prod die Entität, deren zugeordnetes ObjectStateEntry abgefragt wird.
Wird eine Entität vom Objektkontext getrennt (durch Aufruf der Methode Detach), wird das zugehörige ObjectStateEntry-Objekt aus dem Objektkontext entfernt.
Das ObjectStateEntry-Objekt beschreibt mit seiner Eigenschaft State nicht nur den Zustand der zugeordneten Entität (die dafür ihrerseits selbst auch noch die weiter oben angesprochene Eigenschaft EntityState hat, die der Eigenschaft State des zugeordneten ObjectStateEntry-Objekts entspricht). Darüber hinaus werden auch die Referenzen auf das übergeordnete ObjectStateManager-Objekt und das EntitySet geliefert, zu dem die Entität gehört.
Von besonderem Interesse sind insbesondere auch die Werte, die von den Eigenschaften CurrentValues und OriginalValues bereitgestellt werden. OriginalValues enthält dabei die Eigenschaftsdaten, die ursprünglich aus der Datenquelle bezogen worden sind. Sie spiegeln somit Werte des Datensatzes in der Datenbank wider. CurrentValues beschreibt hingegen die aktuellen Daten der Entität im Objektkontext, die unter Umständen vom Anwender geändert worden sind.
Ein Überblick über die wichtigsten Eigenschaften der Klasse ObjectStateEntry ist in Tabelle 39.3 zu sehen.
Eigenschaft | Beschreibung |
CurrentValues |
Ruft die aktuellen Eigenschaftswerte des Objekts ab. Diese Eigenschaft ist im Grunde genommen ein Array und hat den Rückgabetyp DbDataRecord. |
Entity |
Ruft das Objekt ab, das dem ObjectStateEntry zugeordnet ist. |
EntityKey |
Ruft den EntityKey ab, der dem ObjectStateEntry-Objekt zugeordnet ist. |
EntitySet |
Ruft das EntitySet des Objekts ab, das dem ObjectStateEntry-Objekt zugeordnet ist. |
IsRelationship |
Diese Eigenschaft beschreibt einen booleschen Wert, der angibt, ob das ObjectStateEntry-Objekt eine Beziehung darstellt. Die meisten anderen Eigenschaften sind null, wenn diese Eigenschaft auf true gesetzt ist. |
ObjectStateManager |
Ruft den ObjectStateManager des ObjectStateEntry-Objekts ab. |
OriginalValues |
Ruft die originalen Eigenschaftswerte des Objekts ab. Diese Eigenschaft ist im Grunde genommen ein Array und hat den Rückgabetyp DbDataRecord. |
State |
Ruft den Zustand des ObjectStateEntry-Objekts ab. |
Im ersten Kapitel haben Sie erfahren, dass Entitäten durch drei verschiedene Eigenschaften beschrieben werden: skalare Eigenschaften, komplexe Eigenschaften und Navigationseigenschaften. Es sei an dieser Stelle darauf hingewiesen, dass nur skalare Eigenschaften vom ObjectStateEntry-Objekt verfolgt werden, nicht jedoch komplexe Eigenschaften oder Navigationseigenschaften.
39.3.1 Die Current- und Originalwerte abrufen
Das folgende Beispiel zeigt, wie die Originalwerte und aktuellen Werte einer Entität mit den Eigenschaften CurrentValues und OriginalValues abgerufen werden können.
using (NorthwindEntities context = new NorthwindEntities())
{
var cat = (context.Categories
.Where(c => c.CategoryID == 1)
.Select(c => c)).First();
// Kategoriebezeichner ändern
cat.CategoryName = "Getränke";
// ObjectStateEntry abfragen
ObjectStateManager osm = context.ObjectStateManager;
ObjectStateEntry entry = osm.GetObjectStateEntry(cat);
// aktuellen Wert abrufen
DbDataRecord actValues = entry.CurrentValues;
Console.WriteLine("Aktueller Wert: {0}",
actValues.GetValue(actValues.GetOrdinal("CategoryName")));
// Originalwert abrufen
DbDataRecord origValues = entry.OriginalValues;
Console.WriteLine("Originalwert: {0}",
origValues.GetValue(origValues.GetOrdinal("CategoryName")));
}
Listing 39.11 CurrentValue und OriginalValue abrufen
Im Beispiel wird zuerst die zu ändernde Kategorie in den Objektkontext geholt, danach die Eigenschaft CategoryName geändert. Wie wir wissen, kann das ObjectStateEntry-Objekt Auskunft über die aktuellen Werte und die Originalwerte der Eigenschaften liefern. Folglich benötigen wir die Referenz auf das entsprechende ObjectStateEntry-Objekt unserer Entität im Objektkontext. Hier hilft uns die Methode GetObjectStateEntry des ObjectStateManagers weiter, der wir als Argument die Referenz auf die Entität übergeben.
Die Eigenschaft CurrentValues (und im Folgenden auch OriginalValues) liefert ein Objekt vom Typ System.Data.Common.DbDataRecord zurück, mit dem mehrere Einzelwerte zusammengefasst werden. Bei uns ist das die Liste der Eigenschaften der Entität. Aus der gilt es, den Wert der betreffenden Eigenschaft zu ermitteln. Im Grunde genommen reicht dazu die Methode GetValue aus. Allerdings erwartet GetValue einen Integer, der den Ordinalwert der gewünschten Spalte beschreibt. Deshalb rufen wird auf die DbDataRecord deren Methode GetOrdinal auf und übergeben dabei den Eigenschaftsnamen als Zeichenfolge.
Übergeben Sie an GetObjectStateEntry ein Objekt, das nicht zustandsverwaltet wird, hat das die Ausnahme InvalidOperationException zur Folge.
39.3.2 Die Methode »TryGetObjectStateEntry«
Dasselbe Ziel wie die Methode GetObjectStateEntry verfolgt die Methode TryGetObjectStateEntry. Beide unterscheiden sich darin, wie sie auf die Angabe einer nicht vom ObjectStateManager verwalteten Entität reagieren. Während GetObjectStateEntry eine Ausnahme auslöst, gibt TryGetObjectStateEntry einen booleschen Wert zurück, der false ist. Damit eignet sich die Methode sehr gut zur Formulierung einer Bedingung. In Listing 39.12 wird das gezeigt.
using (NorthwindEntities context = new NorthwindEntities())
{
Product prod = new Product(){ ProductName = "Kuchen",
Discontinued = false };
context.AddToProducts(prod);
var osm = context.ObjectStateManager;
ObjectStateEntry entry;
if(osm.TryGetObjectStateEntry(prod, out entry))
{
var ordinal = entry.CurrentValues.GetOrdinal("ProductName");
Console.WriteLine( entry.CurrentValues.GetValue(ordinal));
}
else
Console.WriteLine("Objekt nicht im 'ObjectStateManager'");
}
Listing 39.12 Die Methode »TryGetObjectStateEntry«
Wenn Sie TryGetObjectStateEntry verwenden, müssen Sie vor dem Aufruf eine Variable vom Typ ObjectStateEntry deklarieren. An diese übergibt die Methode über den out-Parameter die Referenz des korrespondierenden ObjectStateEntry-Objekts, falls eine entsprechende Entität gefunden wird.
Sowohl die Methode GetObjectStateEntry als auch die Methode TryGetObjectStateEntry sind überladen. Anstatt der Angabe einer Entität können Sie auch ein Objekt vom Typ EntityKey angeben. Mit dem Typ EntityKey werden wir uns weiter unten beschäftigen.
39.3.3 Abrufen bestimmter Gruppen
Vielleicht sind Sie daran interessiert, welche Entitäten dem Objektkontext hinzugefügt oder geändert worden sind? Vielleicht möchten Sie auch gleichzeitig alle Entitäten abrufen, deren Zustand von Unchanged abweicht. Dazu müssen Sie alle ObjectStateEntry-Objekte, die Ihrer Bedingung entsprechen, aus dem Objektkontext herausfiltern. Auch dabei ist Ihnen eine Methode des ObjectStateManagers behilflich: GetObjectStateEntries. Sie übergeben der Methode einen Member der Enumeration EntityState oder, da diese Enumeration das Attribut Flags hat, mehrere Member, die bitweise kombiniert werden. Der Rückgabewert der Methode ist vom Typ IEnumerable.
Im folgenden Listing wird ein Artikel aus der Tabelle Products abgefragt und dessen Produktbezeichner geändert. Danach wird ein neuer Artikel erzeugt und dem Objektkontext mit der Methode AddToProducts hinzugefügt. Über die Methode GetObjectStateEntries werden alle ObjectStateEntry-Objekte abgefragt, deren Zustand durch Modified oder Added beschrieben wird. Die Ergebnisliste wird in einer Schleife durchlaufen und die Current-Version der Eigenschaft ProductName in die Konsole geschrieben.
using (NorthwindEntities context = new NorthwindEntities())
{
// einen Artikel dem Kontext zuordnen und editieren
var prod = context.Products
.Select(p => p).First(p => p.ProductID == 1);
prod.ProductName = "Spülmittel";
// einen neuen Artikel anlegen
Product product = new Product() { ProductName = "Senf",
Discontinued = false };
context.AddToProducts(product);
// alle Produkte abfragen, die geändert oder hinzugefügt worden sind
ObjectStateManager osm = context.ObjectStateManager;
var items = osm.GetObjectStateEntries(
EntityState.Modified | EntityState.Added);
foreach (var item in items)
{
var values = item.CurrentValues;
Console.WriteLine(values.GetValue(values.GetOrdinal("ProductName")));
}
}
Listing 39.13 Ausgabe aller editierten und hinzugefügten Entitäten
39.3.4 Die Methode »GetModifiedProperties«
Das ObjectStateEntry-Objekt weist noch eine sehr interessante Methode auf: GetModifiedProperties. Die Methode gibt an den Aufrufer eine IEnumerable<string>-Liste der Eigenschaften einer Entität zurück, die sich verändert haben, also als Modified gekennzeichnet sind.
In Listing 39.14 wird der Einsatz der Methode anhand der Änderung von drei Eigenschaften eines Artikels gezeigt.
using (NorthwindEntities context = new NorthwindEntities())
{
var product = (context.Products
.Where(prod => prod.ProductID == 1)
.Select(prod => prod)).First();
product.ProductName = "Orangensaft";
product.UnitPrice = (decimal?)2.98;
product.UnitsInStock = 10;
ObjectStateManager osm = context.ObjectStateManager;
ObjectStateEntry entry = osm.GetObjectStateEntry(product);
// die Liste der veränderten Eigenschaften ausgeben
foreach (var item in entry.GetModifiedProperties())
Console.WriteLine(item);
Console.ReadLine();
}
Listing 39.14 Einsatz der Methode »ObjectStateEntry.GetModifiedProperties«
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.