Utilizzo dei valori delle proprietà

Nella maggior parte dei casi Entity Framework eseguirà il rilevamento dello stato, dei valori originali e dei valori correnti delle proprietà delle istanze dell'entità. Tuttavia, potrebbero verificarsi alcuni casi, ad esempio scenari disconnessi, in cui si vuole visualizzare o modificare le informazioni fornite da Entity Framework sulle proprietà. Le tecniche illustrate in questo argomento si applicano in modo analogo ai modelli creati con Code First ed EF Designer.

Entity Framework tiene traccia di due valori per ogni proprietà di un'entità rilevata. Il valore corrente è, come indicato dal nome, il valore corrente della proprietà nell'entità. Il valore originale è il valore della proprietà quando l'entità è stata sottoposta a query dal database o collegata al contesto.

Esistono due meccanismi generali per l'uso dei valori delle proprietà:

  • Il valore di una singola proprietà può essere ottenuto in modo fortemente tipizzato usando il metodo Property.
  • I valori per tutte le proprietà di un'entità possono essere letti in un oggetto DbPropertyValues. DbPropertyValues funge quindi da oggetto simile a un dizionario per consentire la lettura e l'impostazione dei valori delle proprietà. I valori in un oggetto DbPropertyValues possono essere impostati da valori in un altro oggetto DbPropertyValues o da valori in un altro oggetto, ad esempio un'altra copia dell'entità o un oggetto DTO (Simple Data Transfer Object).

Le sezioni seguenti illustrano esempi di uso di entrambi i meccanismi precedenti.

Recupero e impostazione del valore corrente o originale di una singola proprietà

L'esempio seguente mostra come è possibile leggere il valore corrente di una proprietà e quindi impostare su un nuovo valore:

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";
}

Utilizzare la proprietà OriginalValue anziché la proprietà CurrentValue per leggere o impostare il valore originale.

Si noti che il valore restituito viene digitato come "oggetto" quando viene usata una stringa per specificare il nome della proprietà. D'altra parte, il valore restituito viene fortemente tipizzato se viene usata un'espressione lambda.

Impostando il valore della proprietà come questo, la proprietà verrà contrassegnata come modificata solo se il nuovo valore è diverso dal valore precedente.

Quando un valore della proprietà viene impostato in questo modo, la modifica viene rilevata automaticamente anche se AutoDetectChanges è disattivato.

Recupero e impostazione del valore corrente di una proprietà non mappata

È anche possibile leggere il valore corrente di una proprietà non mappata al database. Un esempio di proprietà non mappata può essere una proprietà RssLink nel blog. Questo valore può essere calcolato in base al BlogId e pertanto non deve essere archiviato nel database. Ad esempio:

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;
}

Il valore corrente può essere impostato anche se la proprietà espone un setter.

La lettura dei valori delle proprietà non mappate è utile quando si esegue la convalida di Entity Framework di proprietà non mappate. Per lo stesso motivo i valori correnti possono essere letti e impostati per le proprietà delle entità che non vengono attualmente rilevate dal contesto. Ad esempio:

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";
}

Si noti che i valori originali non sono disponibili per le proprietà non mappate o per le proprietà delle entità che non vengono rilevate dal contesto.

Verifica se una proprietà è contrassegnata come modificata

L'esempio seguente mostra come verificare se una singola proprietà è contrassegnata come modificata:

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;
}

I valori delle proprietà modificate vengono inviati come aggiornamenti al database quando viene chiamato SaveChanges.

Contrassegno di una proprietà come modificata

L'esempio seguente illustra come forzare la modifica di una singola proprietà:

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;
}

Se si contrassegna una proprietà come modificata, un aggiornamento deve essere inviato al database per la proprietà quando saveChanges viene chiamato anche se il valore corrente della proprietà è uguale al valore originale.

Non è attualmente possibile reimpostare una singola proprietà in modo che non venga modificata dopo che è stata contrassegnata come modificata. Si tratta di un elemento che si prevede di supportare in una versione futura.

Lettura dei valori correnti, originali e del database per tutte le proprietà di un'entità

Nell'esempio seguente viene illustrato come leggere i valori correnti, i valori originali e i valori effettivamente presenti nel database per tutte le proprietà mappate di un'entità.

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]);
    }
}

I valori correnti sono i valori attualmente contenuti nelle proprietà dell'entità. I valori originali sono i valori letti dal database quando è stata eseguita una query sull'entità. I valori del database sono i valori attualmente archiviati nel database. Il recupero dei valori del database è utile quando i valori nel database possono essere stati modificati dopo che è stata eseguita una query sull'entità, ad esempio quando è stata apportata una modifica simultanea al database da un altro utente.

Impostazione dei valori correnti o originali da un altro oggetto

I valori correnti o originali di un'entità rilevata possono essere aggiornati copiando i valori da un altro oggetto. Ad esempio:

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; }
}

L'esecuzione del codice precedente verrà stampata:

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

Questa tecnica viene talvolta usata durante l'aggiornamento di un'entità con valori ottenuti da una chiamata al servizio o da un client in un'applicazione a più livelli. Si noti che l'oggetto usato non deve essere dello stesso tipo dell'entità, purché disponga di proprietà i cui nomi corrispondono a quelli dell'entità. Nell'esempio precedente viene usata un'istanza di BlogDTO per aggiornare i valori originali.

Si noti che solo le proprietà impostate su valori diversi quando vengono copiati dall'altro oggetto verranno contrassegnate come modificate.

Impostazione dei valori correnti o originali da un dizionario

I valori correnti o originali di un'entità rilevata possono essere aggiornati copiando i valori da un dizionario o da un'altra struttura di dati. Ad esempio:

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);
}

Utilizzare la proprietà OriginalValues anziché la proprietà CurrentValues per impostare i valori originali.

Impostazione dei valori correnti o originali da un dizionario tramite Proprietà

Un'alternativa all'uso di CurrentValues o OriginalValues, come illustrato in precedenza, consiste nell'usare il metodo Property per impostare il valore di ogni proprietà. Questo può essere preferibile quando è necessario impostare i valori di proprietà complesse. Ad esempio:

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];
    }
}

Nell'esempio precedente si accede a proprietà complesse usando nomi punteggiati. Per altri modi per accedere a proprietà complesse, vedere le due sezioni più avanti in questo argomento in particolare sulle proprietà complesse.

Creazione di un oggetto clonato contenente valori correnti, originali o di database

L'oggetto DbPropertyValues restituito da CurrentValues, OriginalValues o GetDatabaseValues può essere usato per creare un clone dell'entità. Questo clone conterrà i valori delle proprietà dell'oggetto DbPropertyValues usato per crearlo. Ad esempio:

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

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

Si noti che l'oggetto restituito non è l'entità e non viene monitorato dal contesto. L'oggetto restituito non dispone inoltre di relazioni impostate su altri oggetti.

L'oggetto clonato può essere utile per risolvere i problemi relativi agli aggiornamenti simultanei al database, soprattutto quando viene usata un'interfaccia utente che prevede il data binding agli oggetti di un determinato tipo.

Recupero e impostazione dei valori correnti o originali di proprietà complesse

Il valore di un intero oggetto complesso può essere letto e impostato usando il metodo Property così come può essere per una proprietà primitiva. È anche possibile eseguire il drill-down nell'oggetto complesso e leggere o impostare le proprietà di tale oggetto o persino un oggetto annidato. Di seguito vengono forniti alcuni esempi:

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;
}

Utilizzare la proprietà OriginalValue anziché la proprietà CurrentValue per ottenere o impostare un valore originale.

Si noti che è possibile utilizzare il metodo Property o ComplexProperty per accedere a una proprietà complessa. Tuttavia, il metodo ComplexProperty deve essere utilizzato se si desidera eseguire il drill-down nell'oggetto complesso con chiamate Aggiuntive Property o ComplexProperty.

Uso di DbPropertyValues per accedere a proprietà complesse

Quando si usano CurrentValues, OriginalValues o GetDatabaseValues per ottenere tutti i valori correnti, originali o di database per un'entità, i valori di qualsiasi proprietà complessa vengono restituiti come oggetti DbPropertyValues annidati. Questi oggetti annidati possono quindi essere usati per ottenere i valori dell'oggetto complesso. Ad esempio, il metodo seguente stampa i valori di tutte le proprietà, inclusi i valori di tutte le proprietà complesse e le proprietà complesse annidate.

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]);
        }
    }
}

Per stampare tutti i valori correnti delle proprietà, il metodo verrà chiamato come segue:

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

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