Change Tracker: Working with tracking information (Single Entity)

Description and code samples
Table of Contents
Description

 

Class Method / Property Type Description Comments
DbContext Entry DbEntityEntry<TEntity>
DbEntityEntry
Provides access to tracking information for an entity Implies an implicit call to the method DetectChanges
DbEntityEntry<TEntity>
DbEntityEntry
State EntityState Persistent state of the entity object
CurrentValues DbPropertyValues Gets a dictionary of the current property values for the tracked entity The values of any complex properties are returned as nested DbPropertyValues objects
OriginalValues DbPropertyValues Gets a dictionary of the original property values for the tracked entity The original values are the entity’s property values as they were when last queried from the database.The values of any complex properties are returned as nested DbPropertyValues objects
GetDatabaseValues DbPropertyValues Execute a explicit query to the database and copies the values of the tracked entity as they currently exist in the database and encapsulates the collection into a dictionary If the entity is not found in the database then nullis returnedThe values of any complex properties are returned as nested DbPropertyValues objects
Reload void Reloads the entity from the database overwriting any property values with values from the database The entity state will be set to the Unchanged state
DbPropertyValues GetValue<TValue>(“NAME“) object Gets the value of the parameterized property
ToObject object Copy the current values of the entity into a new instance of the same entity type
Clone DbPropertyValues Creates a new dictionary containing copies of all the properties in this dictionary Changes on the original dicitonary will not be reflected in the clone and vice versa
SetValues(DbPropertyValues) void Sets the values of this dictionary by reading values from another dictionary The other dictionary must be based on the same type as this dictionary, or a type derived
SetValues(Object) void Sets the values of this dictionary by reading values out of the parameterized object of any type Any readable property on the object with a name that matches a property name in the dictionary can be read. Other properties are ignored.
Usefull for copying properties from Data Transfer Objects (DTOs)

 

Operations
How to verify the tracking state of an entity
How to do it
using (var context = new BreakAwayContext())
{
    var canyon = (from d in context.Destinations
                  where d.Name == "Grand Canyon"
                  select d).Single();

    DbEntityEntry<Destination> entry = context.Entry(canyon);

    Console.WriteLine("Before Edit: {0}", entry.State);
    canyon.TravelWarnings = "Take lots of water.";
    Console.WriteLine("After Edit: {0}", entry.State);
}
How to iterate entity values: Current, Original and Database values

TheDbPropertyValues type is used to represent each of the sets of properties:

Current values are the values that are currently set in the properties of the entity;

Original values are the values for each property when the entity was originally attached to the context;

Database values are the values currently stored in the database, which may have changed since queried for the entity. Accessing database values involves Entity Framework performing a explicit query to the database.

How to do it
private static void PrintChangeTrackingInfo(
  BreakAwayContext context,
  Lodging entity)
{
  var entry = context.Entry(entity);

  Console.WriteLine(entry.Entity.Name);

  Console.WriteLine("State: {0}", entry.State);

  Console.WriteLine("\nCurrent Values:");
  PrintPropertyValues(entry.CurrentValues);

  Console.WriteLine("\nOriginal Values:");
  PrintPropertyValues(entry.OriginalValues);

  Console.WriteLine("\nDatabase Values:");
  PrintPropertyValues(entry.GetDatabaseValues());
}

private static void PrintPropertyValues(DbPropertyValues values)
{
  foreach (var propertyName in values.PropertyNames)
  {
    Console.WriteLine(" - {0}: {1}",
      propertyName,
      values[propertyName]);
  }
}
How to copy values into a new instance of an entity
How to do it
 using (var context = new BreakAwayContext())
  {
    var reef = (from d in context.Destinations
                where d.Name == "Great Barrier Reef"
                select d).Single();

    reef.TravelWarnings = "Watch out for sharks!";

    Console.WriteLine("Current Values");
    PrintDestination(reef);

    Console.WriteLine("\nDatabase Values");
    DbPropertyValues dbValues = context.Entry(reef)
      .GetDatabaseValues();

    PrintDestination((Destination)dbValues.ToObject());
  }
How to change values on an entity without the need to call method DetectChanges
Remarks

When doing changes using the Change Tracker API, there is no need for DetectChanges to be called, because the Change Tracker is aware of the changes being made.

How to do it
 using (var context = new BreakAwayContext())
  {
    context.Configuration.AutoDetectChangesEnabled = false;

    var hotel = (from d in context.Lodgings
                 where d.Name == "Grand Hotel"
                 select d).Single();

    context.Entry(hotel)
      .CurrentValues["Name"] = "Hotel Pretentious";

    Console.WriteLine("Property Value: {0}", hotel.Name);
    Console.WriteLine("State: {0}", context.Entry(hotel).State);
  }
How to set Current or Original values from another object
How to do it
using (var context = new BreakAwayContext())
{
    var princess = context.Princesses.Find(1);
    var rapunzel = new Princess { Id = 1, Name = "Rapunzel" };
    var rosannella = new PrincessDTO { Id = 1, Name = "Rosannella" };

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

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

    Console.WriteLine("\nOriginal values:");
    PrintValues(entry.OriginalValues);
}
public class PrincessDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Current values:
Property Id has value 1
Property Name has value Rapunzel

Original values:
Property Id has value 1
Property Name has value Rosannella
How to set Current or Original values from a dictionary
How to do it
using (var context = new BreakAwayContext())
{
    var lady = context.LadiesInWaiting.Find(1, "The EF Castle");

    var newValues = new Dictionary<string, object>
    {
        { "FirstName", "Calypso" },
        { "Title", " Prima donna" },
    };

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

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

    PrintValues(currentValues);
}
public static void PrintValues(DbPropertyValues values)
{
    foreach (var propertyName in values.PropertyNames)
    {
        Console.WriteLine("Property {0} has value {1}",
                          propertyName, values[propertyName]);
    }
}
How to clone a set of values of an entity
How to do it
using (var context = new BreakAwayContext())
  {
    var hotel = (from d in context.Lodgings
                  where d.Name == "Grand Hotel"
                  select d).Single();

    var values = context.Entry(hotel).CurrentValues.Clone();

    values["Name"] = "Simple Hotel";

    Console.WriteLine("Property Value: {0}", hotel.Name);
    Console.WriteLine("State: {0}", context.Entry(hotel).State);
  }
How to copy contents of a set of values (e.g. CurrentValues) into another set (e.g. OriginalValues) within the same instance
How to do it
 using (var context = new BreakAwayContext())
  {
    var canyon = (from d in context.Destinations
                  where d.Name == "Grand Canyon"
                  select d).Single();

    canyon.Name = "Bigger & Better Canyon";

    var entry = context.Entry(canyon);
    entry.CurrentValues.SetValues(entry.OriginalValues);
    entry.State = EntityState.Unchanged;

    Console.WriteLine("Name: {0}", canyon.Name);
  }
Reload an entity from database
Remarks

The Reload method overwrites any changes made at memory level

How to do it
 using (var context = new BreakAwayContext())
  {
    var hotel = (from d in context.Lodgings
                  where d.Name == "Grand Hotel"
                  select d).Single();

    context.Database.ExecuteSqlCommand(
      @"UPDATE dbo.Lodgings
        SET Name = 'Le Grand Hotel'
        WHERE Name = 'Grand Hotel'");

    Console.WriteLine(
      "Name Before Reload: {0}",
      hotel.Name);

    Console.WriteLine(
      "State Before Reload: {0}",
      context.Entry(hotel).State);

    context.Entry(hotel).Reload();

    Console.WriteLine(
      "Name After Reload: {0}",
      hotel.Name);

    Console.WriteLine(
      "State After Reload: {0}",
      context.Entry(hotel).State);
  }
}
How to verify if an entity is already tracked
How to do it

Option 1: Using DbContext:

if (context.Destinations.Local.Any(e => e.Name == "Funchal")) {
   Console.Write("Destination is attached to the context");
}
if (context.Destinations.Local.Any(e => e.Id == 10)) {
   Console.Write("Destination is attached to the context");
}

Option 2: Using DbContext and the tracking mechanism:

if (context.ChangeTracker.Entries<Destination>().Any(e => (e.Entity As Destination).Name == "Funchal"))
{
   Console.Write("Destination is attached to the context");
}
if (context.ChangeTracker.Entries<Destination>().Any(e => e.Entity.Id == 10))
{
   Console.Write("Destination is attached to the context");
}