Loading presentation...

Present Remotely

Send the link below via email or IM


Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.


Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Entity Framework Code First: Change Tracker API

Using the Change Tracker API in Entity Framework Code First

Ian Lovell

on 16 November 2012

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Entity Framework Code First: Change Tracker API

Obstacle 1 A thing The End Start Some example usages of the Change Tracker API in a real life scenario are:
Resolving concurrency conflicts
Logging changes to entities for auditing purposes Logging During Saving Entity Framework Code First Change Tracker API A fish. Using the Change Tracker API in Applications As well as dealing with concurrency issues, the change tracking information provided by the Change Tracker API is useful to log changes made to entities when the user saves.
The easiest way to run additional logic during the save process is to override the SaveChanges method on your derived context. Change Tracker API This session is about accessing the information that Entity Framework stores about the entities it is tracking.
This includes:
The values stored for the properties in the entity
The current state of the entity
The original values from the database
Which properties have been modified
The Change Tracker API also gives you access to operations that can be performed on the entity. The Entry Method The easiest way to get access to change tracking information for an entity is with the Entry method on DbContext.

Entry returns a DbEntityEntry instance

This gives you access to the information and operations available for the entity. var department = ...;
DbEntityEntry<Department> entry = context.Entry(department); The State Property The State property returns the current state of an entity
It can also be set, which is useful for working with disconnected entities particularly in N-tier scenarios
If you wish for the state to be updated immediately on a property change, then you must be working with change tracking proxies
Otherwise, a call to DetectChanges is required.
This happens implicitly with many DbContext API calls, including the Entry method itself Current, Original, and Database Values Entry also gives you access to the entity's current, original, and database values.
These are represented as an instance of DbPropertyValues
Original Values
The values for each property when the entity was originally attached to the context
Database Values
The values currently stored in the database, which may have changed since you queried for the entity
Accessing these requires a behind-the-scenes query
Current Values
The current values stored in the entity
New entities do not have original values or database values
Entities marked for deletion do not have current values Copying DbPropertyValues to an object Sometimes you may want the original or database values of an entity as an entity type itself, rather than a DbPropertyValues
This might be the case if you wish to display the original or database values to the user
DbPropertyValues includes a ToObject method that will copy the values into a new instance of the entity Current, Original, and Database Values PrintProperties(entry.CurrentValues);

private void PrintProperties(DbPropertyValues values)
foreach (var propertyName in values.PropertyNames)
Console.WriteLine("{0} {1}", propertyName, values[propertyName];
} Modifying DbPropertyValues DbPropertyValues isn't read only
You can use it to update values that are stored in an instance
When you set values using CurrentValues or OriginalValues, this will also update the current and original values in the change tracker.
Updating the CurrentValues will also change the values stored in the properties of your entity instance.
If you make changes using the Change Tracker API, there is no need for DetectChanges to be called, because the change tracker is aware of the change being made. Cloning DbPropertyValues You can clone a DbPropertyValues.
This is useful for times when you don't want changes to be recorded in the change tracker.
The Clone method will return a copy of any DbPropertyValues instance.
When you clone current or original values, the resulting copy will not be tracked by the change tracker. Using SetValues You can copy the contents of one DbPropertyValues into another using the SetValues method.
For example, you may want to undo edits to an entity by copying the original values back over the current values. Working with Individual Properties You can access individual properties using DbPropertyValues, but that uses string-based names for the property.
You want to use lambda expressions to identify properties so that you get compile-time checking, Intellisense, and refactoring support.
You can use the Property, Complex, Reference, and Collection methods to get access to individual property information and operations.
Used for scalar and complex properties
Get additional operations specific to complex properties
Reference and Collection
Used for navigation properties DbEntityEntry<Department> entry = context.Entry(department);
department.Name = "Another name";
Console.WriteLine(entry.State); DbPropertyValues dbValues = context.Entry(department).GetDatabaseValues();
var newDestination = dbValues.ToObject() as Destination; ToObject will only clone the values from scalar properties; all navigation properties will be left unassigned.
It's useful for cloning a single object, but not an entire object graph context.Entry(department).CurrentValues["Name"] = "Another name"; var department = ...;
var values = context.Entry(department).CurrentValues.Clone();

values["Name"] = "Another name";

Console.WriteLine(department.Name); var entry = context.Entry(department);
entry.State = EntityState.Unchanged; Entity Framework isn't smart enough to detect that these new values match the original values, so the code also manually swaps the state back to Unchanged. The Property Method The Property method allows you to read and write the original and current value of a property.
It also lets you know whether an individual property is marked at Modified var entry = context.Entry(department);
entry.Property(d => d.Name).CurrentValue = "New value";
Console.WriteLine(entry.Property(d => d.Name).OriginalValue);
Console.WriteLine(entry.Property(d => d.Name).IsModified); Enumerating Property Modifications All of the property methods as well as taking a lambda, also provide an overload which takes the property name as a string.
You can use this in combination with the DbPropertyValues.PropertyNames collection to get a list of modified properties for an entity. var entry = context.Entry(department);
var propertyNames = entry.CurrentValues.PropertyNames;

IEnumerable<string> modifiedProperties =
propertyNames.Where(n => entry.Property(n).IsModified); Working With Complex Properties The ComplexProperty method gives you access to change tracking information and operations.
The same operations are available for complex properties that are available for scalar properties.
You can also use the Property method to drill into individual scalar properties on the complex type. var entry = context.Entry(department);
entry.ComplexProperty(d => d.Address)
.Property(a => a.Street)
.CurrentValue = "Another street"; Working With Navigation Properties With navigation properties, you use the Collection or Reference methods instead of the Property method.
Reference is used when the navigation property is just a reference to a single entity.
Collection is used when the navigation property is a collection.
These methods give you the ability to:
Read and write the current value assigned to the navigation property
Load the related data from the database.
Get a query representing the contents of the navigation property
There is also an IsLoaded property which signals whether the related data has been loaded. You can chain calls to ComplexProperty if your complex property also contains a complex property.
You can also use the dot syntax to specify a complex property: var isModified = entry.Property(d => d.Address.Street).IsModified; Modifying the Value of a Navigation Property The CurrentValue property gives you the current value for a property.
You can use it to change the entity or collection assigned to navigation property.
The change tracker will take care of relationship fixup. var entry = context.Entry(department);

var oldName = entry.Reference(d => d.Location).CurrentValue.Name;
entry.Reference(d => d.Location).CurrentValue = anotherLocation;

// location.Departments also updated Working with Collection Navigation Properties The same operations that are available for reference navigation properties are also available for collection navigation properties.
Unlike scalar and reference navigation properties, collection navigation properties do not automatically perform relationship fixup when changing the current value.
This is not the case though if you are using change tracking proxies. entry.Collection(d => d.Employees).Load();
var count = entry.Collection(d => d.Employees).CurrentValue.Count;
entry.Collection(d => d.Employees).CurrentValue.Add(employee); Refreshing an Entity from the Database Entity Framework includes a Reload method on DbEntityEntry that can be used to refresh an entity with the latest data from the database.
Reload will overwrite any changes you have in memory. var department = context.Departments.Find(5);

context.Database.ExecuteSqlCommand(@"UPDATE dbo.Departments SET Name = 'Another name' WHERE Id = 5");

department.Name = "My local name";
// outputs original database value, then 'Another name' Change Tracking Information and Operations for Multiple Entities DbContext.Entry gets the DbEntityEntry for a single entity
If you wish to get change tracking information about multiple entities, you can use the DbContext.ChangeTracker.Entries method.
There is a generic Entries<TEntity> method which returns a collection of DbEntityEntry<TEntity> for all entities of the type of TEntity.
There is also a general Entries method which returns all entities tracked by the change tracker (all entity types). Entries Method Example context.Entry(department).Collection(d => e.Employees).Load();

department.Employees.Add(new Employees { Name = "Fred" };

var entries = context.ChangeTracker.Entries();
foreach (var entry in entries)

// Get unchanged employees
var unchanged = context.ChangeTracker.Entries<Employee>().Where(e => e.State == EntityState.Unchanged); Resolving Concurrency Conflicts A concurrency conflict occurs when you attempt to update a record in the database but another user has updated that same record since you queried for it.
By default, Entity Framework will just update the properties that you have modified regardless if they have changed in the database.
Entity Framework supports optimistic locking out of the box, it doesn't support pessimistic locking.
Optimistic concurrency/locking assumes that resource conflicts are unlikely and only checks for conflicts when attempting to change data.
Pessimistic concurrency/locking locks resources as they are required, for the duration of the transaction.
You can configure optimistic concurrency control in Entity Framework using a property on your entities that is a concurrency token. Optimistic Concurrency Entity Framework has supported optimistic concurrency since the first version.
Typically you add a column in your database relation (table) which is used to mark the version of that entity.
This column type is normally timestamp in SQL Server.
In SQL Server 2008 the timestamp data type was changed to rowversion (a more suitable name) to match the standard used in other RDBMS's.
However, SQL Server Management Studio, Visual Studio and other tools still display it as 'timestamp'.
With Code First, you can configure any database column to be used as a concurrency field, but typically you would want to map this to a timestamp/rowversion column.
Your entity property type must be a byte array. public byte[] RowVersion { get; set; }

modelBuilder.Entity<Department>.Property(d => d.RowVersion).IsRowVersion(); The Effect of the TimeStamp/RowVersion When a table has a timestamp column, the database will automatically generate a new value for that column whenever any other column in the record is updated.
When Entity Framework does a query or an insert (Add) of an entity, it retrieves the database generated timestamp value.
When it comes to perform an update or delete (on SaveChanges), Entity Framework uses the timestamp value read and compares it to the one currently stored for that entity.
If they differ, then a concurrency conflict has occurred, and Entity Framework throws an OptimisticConcurrencyException.
You can handle this in the client and decide how to deal with it.
For example you could tell the user that their changes have been cancelled, or you could attempt to merge the user's changes with the stored database values (providing a 'diff' interface to the user) Handling an OptimisticConcurrencyException The concurrency exception gives you access to everything you need to know about the conflict.
The exception's Entries property gives you the DbEntityEntry for each of the entities that had a concurrency conflict. try
catch (DbUpdateConcurrencyException exception)
foreach (var entry in exception.Entries)
// entry.CurrentValues, entry.OriginalValues, entry.GetDatabaseValues()
} public class MyContext : DbContext
public override int SaveChanges()
var modifiedEntries = this.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged);
foreach (var entry in modifiedEntries)
// get access to entry.State, entry.CurrentValues etc
return base.SaveChanges();
Full transcript