Arbeiten mit Eigenschaftswerten

In den meisten Fällen übernimmt Entity Framework das Nachverfolgen des Zustands, der ursprünglichen Werte und der aktuellen Werte der Eigenschaften Ihrer Entitätsinstanzen. Es kann jedoch Fälle geben – z. B. unzusammenhängende Szenarien –, in denen Sie die Informationen, die EF über die Eigenschaften hat, anzeigen oder manipulieren möchten. Die in diesem Thema dargestellten Techniken gelten jeweils für Modelle, die mit Code First und dem EF-Designer erstellt wurden.

Entity Framework verfolgt für jede Eigenschaft einer nachverfolgten Entität zwei Werte. „Aktueller Wert“, wie der Name angibt, der aktuelle Wert der Eigenschaft in der Entität. Der ursprüngliche Wert ist der Wert, den die Eigenschaft hatte, als die Entität aus der Datenbank abgefragt oder dem Kontext zugefügt wurde.

Es gibt zwei allgemeine Mechanismen für das Arbeiten mit Eigenschaftswerten:

  • Der Wert einer einzelnen Eigenschaft kann mithilfe der Property-Methode stark typisiert abgerufen werden.
  • Werte für alle Eigenschaften einer Entität können in ein DbPropertyValues-Objekt gelesen werden. DbPropertyValues fungiert dann als ein wörterbuchähnliches Objekt, damit Eigenschaftswerte gelesen und festgelegt werden können. Die Werte in einem DbPropertyValues-Objekt können aus Werten in einem anderen DbPropertyValues-Objekt oder aus Werten in einem sonstigen Objekt festgelegt werden, z. B. eine andere Kopie der Entität oder ein einfaches Datenübertragungsobjekt (Data Transfer Object, DTO).

In den folgenden Abschnitten finden Sie Beispiele für die Verwendung der beiden oben genannten Mechanismen.

Abrufen und Festlegen des aktuellen oder ursprünglichen Werts einer einzelnen Eigenschaft

Das folgende Beispiel zeigt, wie der aktuelle Wert einer Eigenschaft gelesen und dann auf einen neuen Wert festgelegt werden kann:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(3);

    // Read the current value of the Name property
    string currentName1 = context.Entry(blog).Property(u => u.Name).CurrentValue;

    // Set the Name property to a new value
    context.Entry(blog).Property(u => u.Name).CurrentValue = "My Fancy Blog";

    // Read the current value of the Name property using a string for the property name
    object currentName2 = context.Entry(blog).Property("Name").CurrentValue;

    // Set the Name property to a new value using a string for the property name
    context.Entry(blog).Property("Name").CurrentValue = "My Boring Blog";
}

Verwenden Sie die OriginalValue-Eigenschaft anstelle der CurrentValue-Eigenschaft, um den ursprünglichen Wert zu lesen oder festzulegen.

Beachten Sie, dass der zurückgegebene Wert als „Objekt“ eingegeben wird, wenn eine Zeichenfolge zum Angeben des Eigenschaftennamens verwendet wird. Andererseits wird der zurückgegebene Wert stark eingegeben, wenn ein Lambda-Ausdruck verwendet wird.

Wenn Sie den Eigenschaftswert wie folgt festlegen, wird die Eigenschaft nur als geändert markiert, wenn sich der neue Wert vom alten Wert unterscheidet.

Wenn ein Eigenschaftswert auf diese Weise festgelegt wird, wird die Änderung automatisch erkannt, auch wenn AutoDetectChanges deaktiviert ist.

Abrufen und Festlegen des aktuellen Werts einer nicht zugeordneten Eigenschaft

Der aktuelle Wert einer Eigenschaft, die der Datenbank nicht zugeordnet ist, kann auch gelesen werden. Ein Beispiel für eine nicht zugeordnete Eigenschaft könnte eine RssLink-Eigenschaft im Blog sein. Dieser Wert kann basierend auf der BlogId berechnet werden und muss daher nicht in der Datenbank gespeichert werden. Beispiel:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    // Read the current value of an unmapped property
    var rssLink = context.Entry(blog).Property(p => p.RssLink).CurrentValue;

    // Use a string to specify the property name
    var rssLinkAgain = context.Entry(blog).Property("RssLink").CurrentValue;
}

Der aktuelle Wert kann auch festgelegt werden, wenn die Eigenschaft einen Setter verfügbar macht.

Das Lesen der Werte von nicht zugeordneten Eigenschaften ist nützlich, wenn eine Entity Framework-Überprüfung von nicht zugeordneten Eigenschaften ausgeführt wird. Aus demselben Grund können aktuelle Werte für Eigenschaften von Entitäten gelesen und festgelegt werden, die derzeit nicht vom Kontext nachverfolgt werden. Beispiel:

using (var context = new BloggingContext())
{
    // Create an entity that is not being tracked
    var blog = new Blog { Name = "ADO.NET Blog" };

    // Read and set the current value of Name as before
    var currentName1 = context.Entry(blog).Property(u => u.Name).CurrentValue;
    context.Entry(blog).Property(u => u.Name).CurrentValue = "My Fancy Blog";
    var currentName2 = context.Entry(blog).Property("Name").CurrentValue;
    context.Entry(blog).Property("Name").CurrentValue = "My Boring Blog";
}

Beachten Sie, dass originale Werte für nicht zugeordnete Eigenschaften oder für Eigenschaften von Entitäten, die nicht vom Kontext nachverfolgt werden, nicht verfügbar sind.

Überprüfen, ob eine Eigenschaft als geändert markiert ist

Das folgende Beispiel zeigt, wie Sie überprüfen können, ob eine einzelne Eigenschaft als geändert markiert ist:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    var nameIsModified1 = context.Entry(blog).Property(u => u.Name).IsModified;

    // Use a string for the property name
    var nameIsModified2 = context.Entry(blog).Property("Name").IsModified;
}

Die Werte der geänderten Eigenschaften werden als Updates an die Datenbank gesendet, wenn SaveChanges aufgerufen wird.

Markieren einer Eigenschaft als geändert

Das folgende Beispiel zeigt, wie sie erzwingen, dass eine einzelne Eigenschaft als geändert markiert wird:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    context.Entry(blog).Property(u => u.Name).IsModified = true;

    // Use a string for the property name
    context.Entry(blog).Property("Name").IsModified = true;
}

Wenn eine Eigenschaft als geändert markiert wird, wird erzwungen, dass ein Update an die Datenbank für die Eigenschaft gesendet wird, wenn SaveChanges aufgerufen wird, auch wenn der aktuelle Wert der Eigenschaft mit dem ursprünglichen Wert übereinstimmt.

Es ist derzeit nicht möglich, eine einzelne Eigenschaft zurückzusetzen, die nicht geändert wird, nachdem sie als geändert markiert wurde. Dies ist etwas, das wir in einer zukünftigen Version unterstützen möchten.

Lesen aktueller, ursprünglicher und Datenbankwerte für alle Eigenschaften einer Entität

Das folgende Beispiel zeigt, wie die aktuellen Werte, die ursprünglichen Werte und die Werte in der Datenbank für alle zugeordneten Eigenschaften einer Entität gelesen werden.

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Make a modification to Name in the tracked entity
    blog.Name = "My Cool Blog";

    // Make a modification to Name in the database
    context.Database.SqlCommand("update dbo.Blogs set Name = 'My Boring Blog' where Id = 1");

    // Print out current, original, and database values
    Console.WriteLine("Current values:");
    PrintValues(context.Entry(blog).CurrentValues);

    Console.WriteLine("\nOriginal values:");
    PrintValues(context.Entry(blog).OriginalValues);

    Console.WriteLine("\nDatabase values:");
    PrintValues(context.Entry(blog).GetDatabaseValues());
}

public static void PrintValues(DbPropertyValues values)
{
    foreach (var propertyName in values.PropertyNames)
    {
        Console.WriteLine("Property {0} has value {1}",
                          propertyName, values[propertyName]);
    }
}

Die aktuellen Werte sind die Werte, die die Eigenschaften der Entität derzeit enthalten. Die ursprünglichen Werte sind die Werte, die aus der Datenbank gelesen wurden, als die Entität abgefragt wurde. Datenbankwerte sind die Werte, die derzeit in der Datenbank gespeichert sind. Das Abrufen der Datenbankwerte ist nützlich, wenn sich die Werte in der Datenbank möglicherweise geändert haben, da die Entität abgefragt wurde, z. B. wenn eine gleichzeitige Bearbeitung der Datenbank von einem anderen Benutzer vorgenommen wurde.

Festlegen aktueller oder ursprünglicher Werte aus einem anderen Objekt

Die aktuellen oder ursprünglichen Werte einer nachverfolgten Entität können aktualisiert werden, indem Werte aus einem anderen Objekt kopiert werden. Beispiel:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    var coolBlog = new Blog { Id = 1, Name = "My Cool Blog" };
    var boringBlog = new BlogDto { Id = 1, Name = "My Boring Blog" };

    // Change the current and original values by copying the values from other objects
    var entry = context.Entry(blog);
    entry.CurrentValues.SetValues(coolBlog);
    entry.OriginalValues.SetValues(boringBlog);

    // Print out current and original values
    Console.WriteLine("Current values:");
    PrintValues(entry.CurrentValues);

    Console.WriteLine("\nOriginal values:");
    PrintValues(entry.OriginalValues);
}

public class BlogDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Wenn Sie den obigen Code ausführen, wird Folgendes ausgegeben:

Current values:
Property Id has value 1
Property Name has value My Cool Blog

Original values:
Property Id has value 1
Property Name has value My Boring Blog

Diese Technik wird manchmal verwendet, wenn eine Entität mit Werten aktualisiert wird, die über einen Dienstaufruf oder von einem Client in einer n--schichtigen Anwendung abgerufen wurden. Beachten Sie, dass das verwendete Objekt nicht denselben Typ wie die Entität aufweisen muss, solange es Eigenschaften aufweist, deren Namen mit denen der Entität übereinstimmen. Im obigen Beispiel wird eine Instanz von BlogDTO verwendet, um die ursprünglichen Werte zu aktualisieren.

Beachten Sie, dass nur Eigenschaften, die beim Kopieren vom anderen Objekt auf andere Werte gesetzt werden, als geändert markiert werden.

Festlegen aktueller oder ursprünglicher Werte aus einem Wörterbuch

Die aktuellen oder ursprünglichen Werte einer nachverfolgten Entität können aktualisiert werden, indem Werte aus einem Wörterbuch oder einer anderen Datenstruktur. Beispiel:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    var newValues = new Dictionary<string, object>
    {
        { "Name", "The New ADO.NET Blog" },
        { "Url", "blogs.msdn.com/adonet" },
    };

    var currentValues = context.Entry(blog).CurrentValues;

    foreach (var propertyName in newValues.Keys)
    {
        currentValues[propertyName] = newValues[propertyName];
    }

    PrintValues(currentValues);
}

Verwenden Sie die OriginalValues-Eigenschaft anstelle der CurrentValues-Eigenschaft, um den ursprünglichen Wert festzulegen.

Festlegen aktueller oder ursprünglicher Werte aus einem Wörterbuch mithilfe von „Property“

Eine Alternative zur Verwendung von CurrentValues oder OriginalValues wie oben gezeigt ist die Verwendung der Property-Methode zum Festlegen des Werts jeder Eigenschaft. Dies kann vorzuziehen sein, wenn Sie die Werte komplexer Eigenschaften festlegen müssen. Beispiel:

using (var context = new BloggingContext())
{
    var user = context.Users.Find("johndoe1987");

    var newValues = new Dictionary<string, object>
    {
        { "Name", "John Doe" },
        { "Location.City", "Redmond" },
        { "Location.State.Name", "Washington" },
        { "Location.State.Code", "WA" },
    };

    var entry = context.Entry(user);

    foreach (var propertyName in newValues.Keys)
    {
        entry.Property(propertyName).CurrentValue = newValues[propertyName];
    }
}

Im obigen Beispiel werden mithilfe gepunkteter Namen auf komplexe Eigenschaften zugegriffen. Weitere Möglichkeiten für den Zugriff auf komplexe Eigenschaften finden Sie in den beiden Abschnitten weiter unten in diesem Thema speziell zu komplexen Eigenschaften.

Erstellen eines geklonten Objekts mit aktuellen, ursprünglichen oder Datenbankwerten

Das DbPropertyValues-Objekt, das von CurrentValues, OriginalValues oder GetDatabaseValues zurückgegeben wird, kann zum Erstellen eines Klons der Entität verwendet werden. Dieser Klon enthält die Eigenschaftswerte aus dem DbPropertyValues-Objekt, das zum Erstellen verwendet wird. Beispiel:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject();
}

Beachten Sie, dass das zurückgegebene Objekt nicht die Entität ist und nicht vom Kontext nachverfolgt wird. Das zurückgegebene Objekt verfügt auch nicht über Beziehungen, die auf andere Objekte festgelegt sind.

Das geklonte Objekt kann nützlich sein, um Probleme im Zusammenhang mit parallelen Aktualisierungen der Datenbank zu beheben, insbesondere bei der Verwendung einer Benutzeroberfläche mit Datenbindung an Objekte eines bestimmten Typs.

Abrufen und Festlegen der aktuellen oder ursprünglichen Werte komplexer Eigenschaften

Der Wert eines gesamten komplexen Objekts kann mithilfe der Property-Methode so gelesen und festgelegt werden, wie es für eine primitive Eigenschaft sein kann. Darüber hinaus können Sie das komplexe Objekt aufschlüsseln und Eigenschaften dieses Objekts oder sogar eines verschachtelten Objekts lesen oder festlegen. Hier sehen Sie einige Beispiele:

using (var context = new BloggingContext())
{
    var user = context.Users.Find("johndoe1987");

    // Get the Location complex object
    var location = context.Entry(user)
                       .Property(u => u.Location)
                       .CurrentValue;

    // Get the nested State complex object using chained calls
    var state1 = context.Entry(user)
                     .ComplexProperty(u => u.Location)
                     .Property(l => l.State)
                     .CurrentValue;

    // Get the nested State complex object using a single lambda expression
    var state2 = context.Entry(user)
                     .Property(u => u.Location.State)
                     .CurrentValue;

    // Get the nested State complex object using a dotted string
    var state3 = context.Entry(user)
                     .Property("Location.State")
                     .CurrentValue;

    // Get the value of the Name property on the nested State complex object using chained calls
    var name1 = context.Entry(user)
                       .ComplexProperty(u => u.Location)
                       .ComplexProperty(l => l.State)
                       .Property(s => s.Name)
                       .CurrentValue;

    // Get the value of the Name property on the nested State complex object using a single lambda expression
    var name2 = context.Entry(user)
                       .Property(u => u.Location.State.Name)
                       .CurrentValue;

    // Get the value of the Name property on the nested State complex object using a dotted string
    var name3 = context.Entry(user)
                       .Property("Location.State.Name")
                       .CurrentValue;
}

Verwenden Sie die OriginalValue-Eigenschaft anstelle der CurrentValue-Eigenschaft, um einen ursprünglichen Wert abzurufen oder festzulegen.

Beachten Sie, dass entweder die Eigenschaft oder die ComplexProperty-Methode verwendet werden kann, um auf eine komplexe Eigenschaft zuzugreifen. Die ComplexProperty-Methode muss jedoch verwendet werden, wenn Sie das komplexe Objekt mit zusätzlichen Property- oder ComplexProperty-Aufrufen aufschlüsseln möchten.

Verwenden von DbPropertyValues für den Zugriff auf komplexe Eigenschaften

Wenn Sie CurrentValues, OriginalValues oder GetDatabaseValues verwenden, um alle aktuellen, ursprünglichen oder Datenbankwerte für eine Entität abzurufen, werden die Werte aller komplexen Eigenschaften als geschachtelte DbPropertyValues-Objekte zurückgegeben. Diese geschachtelten Objekte können dann verwendet werden, um Werte des komplexen Objekts abzurufen. Die folgende Methode druckt beispielsweise die Werte aller Eigenschaften, einschließlich der Werte aller komplexen Eigenschaften und geschachtelten komplexen Eigenschaften.

public static void WritePropertyValues(string parentPropertyName, DbPropertyValues propertyValues)
{
    foreach (var propertyName in propertyValues.PropertyNames)
    {
        var nestedValues = propertyValues[propertyName] as DbPropertyValues;
        if (nestedValues != null)
        {
            WritePropertyValues(parentPropertyName + propertyName + ".", nestedValues);
        }
        else
        {
            Console.WriteLine("Property {0}{1} has value {2}",
                              parentPropertyName, propertyName,
                              propertyValues[propertyName]);
        }
    }
}

Um alle aktuellen Eigenschaftswerte auszudrucken, würde die Methode wie folgt aufgerufen:

using (var context = new BloggingContext())
{
    var user = context.Users.Find("johndoe1987");

    WritePropertyValues("", context.Entry(user).CurrentValues);
}