Entity Framework Code First - Change Tracking
In this post we will be discussing about change tracking feature of Entity Framework Code First. Change tracking allows Entity framework to keep track of all the changes in entities' data. It might involve adding new entities to entities collection or modifying or removing existing entities. These changes are kept at DbContextlevel. All changes are lost if they are not saved before destroying the DbContext instance.
By default, Entity framework Code First registers all changes as the occur. When it's time to save those changes it just looks at this information and updates the database tables based on this registered information. Additionally, it keeps a snapshot of entities as they are loaded from Database or when they were last saved to the Database. This snapshot of entities and automatic change tracking are used to push the entities changes to the database.
Enabling Change Tracking
Automatic Change tracking is enabled by default. Disabling it would not trigger the DbContext update for each change in the entity. There are specific instances when DbContext would update the DbContext with the changes. This includes explicit call to SaveChanges() method of DbContext. Change tracking can be enabled / disabled by setting AutoDetectChangesEnabled to true / false respectively for DbContext.
public InstituteEntities()
{
this.Configuration.AutoDetectChangesEnabled = true;
}
This ensures that all the changes involved with any entity in the context are tracked by the framework. The framework maintains the state of entities. It uses this to determine the changes needed to be pushed to database when SaveChanges() is called. Disabling change tracking would still allow us to check the old and current values of an entity but it would keep the state as UnChanged until the changes are detected. We need to manually call DetectChanges() on DbContext to update them. There are instances in which it is called implicitly by Entity Framework API. Since it would check all the entities of all types, for which change tracking is enabled, to verify if they have any changes in their data. If so, it changes their state as Modified. It helps the framework to push all the entities with Added, Modified andDetached states when SaveChanges() is called on DbContext. Here SaveChanges() is an implementation of UoW (Unit of Work) pattern descibed by Martin Fowler.
Change Tracking Proxies Vs Snapshot Change Tracking
Entity Framework creates a snaphot of all entities data when they are loaded from Database. When it needs to save these entities to the database, it compares this snapshot of all entities to their current state. It then updates the database based on the state of these entities. It might add, update or delete the entities based on their states. Saving entities is the implementation of Unit of Work (UoW) pattern described by Martin Fowler. Since it uses the states of entities to perform the specificCRUD [CReate, Update, Delete] operation, the state must be updated before the entities are saved. In order to save developers, the EF implcitly updates the states before saving them, if required. This might be very costly if there are many changes to the entities. In order to optimize this, it is better to callDbContext.DetectChanges() when it is safe to do so. After pushing these changes to the database, another snapshot is taken which is used as OriginalValues of these entities.
There are also other instances when DbContext.DetectChanges() is implicitly called by the framework. According to MSDN, they are as follows:
- The Add, Attach, Find, Local, or Remove members on DbSet
- The GetValidationErrors, Entry, or SaveChanges members on DbContext
- The Entries method on DbChangeTracker
This is unlike ObjectContext API where only SaveChanges() would implicitly call DetectChanges(). In all other instances we need to manually call it if so desired.
Entity States
An entity goes through various states throughout its lifetime. It is based on various operations performed to change its properties. The operations on DbContext can also result in updating the state of an entity. It can roughly be presented in a state diagram as follows:

These are the main state transitions which you might expect in realistic scnenarios. There are a few other state transitions too which are not very realistic e.g. If an entity is added using DbSet.Add() and the entity is already existing then the state of the entity changes to Added. An entity is an object and it is semantically wrong. In a typical implementation, one would expect an exception in this case. Change tracking keeps an entity's state always updated. If we modify any property of an entity its state changes to Modified.
Let's see the effect of enabling / disabling the auto-detection of changes of entities. Let's execute the same code block under both conditions and see the effect of setting the auto-detection to an appropriate value. Here we are updating the DepartmentName of the first department found in the Departments entities collection and setting it to an updated value. We are using DbContext to get the original and updated value of the particular Department entity. Then we are printing the original and updated value of the Department entity along with the state information of these entities. The generic DbContext.Entry() method lets us get theDbEntityEntry object for a particular entity. This is the central type in DbContext API. Once we get it, we can access OriginalValues and CurrentValues properties. Both of them are of type DbPropertyValues. It is a collection of all the properties of an underlying entity or a complex object. Now we can use GetValue<T> method to get the value of any property by providing its name.
using (var context = new InstituteEntities())
{
var department = context.Departments.First<Department>();
department.DepartmentName = "Computer & Information Systems Engineering"; DbEntityEntry<Department> departmentEntry = context.Entry<Department>(department);
DbPropertyValues originalDepartment = departmentEntry.OriginalValues;
DbPropertyValues updatedDepartment = departmentEntry.CurrentValues;
EntityState state = context.Entry<Department>(department).State; Console.WriteLine(
string.Format("State: {0}, Old Value: {1}, New Value: {2}",
state, originalDepartment.GetValue<string>("DepartmentName"),
updatedDepartment.GetValue<string>("DepartmentName")));
}
Here we have enabled auto detection. As you can see updating Department's name updates the state of the entity as Modified.
public InstituteEntities()
{
this.Configuration.AutoDetectChangesEnabled = true;
}

On the contrary, if we disable change tracking the state of an entity is not updated until it is caused. We can do that by calling DbContext.DetectChanges(). It is also implicitly called by EF when SaveChanges() is called. In the following example, we are disabling change tracking. You can see that, although, we can still see old and new values of different properties of an entity, it keeps its state as Unchanged.
public InstituteEntities()
{
this.Configuration.AutoDetectChangesEnabled = false;
}

Note: As you can notice that, although the state of the entity is affected by updating the auto-detection but it's data seems to be reflecting the changes. This is due to the implicit call to DbContext.DetectChanges() by DbContext.Entry() as discussed above.
Capturing Data Changes in Code
In order to capture any changes with the data, Entity Framework has provided a comprehensive API around it. A number of types are provided. They are mostly inSystem.Data.Entity.Infrastructure namespace in EntityFramework assembly. The main type that you would mostly be needing is DbEntityEntry. It is available in both generic and non-generic flavors. Using the generic version saves us from a lot of typecasting later in the code. We can get the DbEntityEntry for any entry. It allows us to check the original values of the properties of an entity. It also provides the state of entity. As we discussed above that this state would depend on a number of factors i.e. wheter the change tracking is enabled or DbContext.DetectChanges() has been called. Now how to get DbEntityEntry for a particular entity. We seem to have a few options here.
Using DbContext.Entry<TEntity>
We use this we are already holding the entity object and we need to obtain information about entity. In the following example we are getting an arbitrary department from the DbContext. We are updating the value of DepartmentName property. DbEntityEntry allows us to get the old and new values of an entity's property asDbPropertyValues. This can be used to get the value of any property using its GetValue method. As you have seen above:
using (var context = new InstituteEntities())
{
var department = context.Departments.First<Department>();
department.DepartmentName = "Computer & Information Systems Engineering"; DbEntityEntry<Department> departmentEntry = context.Entry<Department>(department);
DbPropertyValues originalDepartment = departmentEntry.OriginalValues;
DbPropertyValues updatedDepartment = departmentEntry.CurrentValues;
EntityState state = context.Entry<Department>(department).State; Console.WriteLine(
string.Format("State: {0}, Old Value: {1}, New Value: {2}",
state, originalDepartment.GetValue<string>("DepartmentName"),
updatedDepartment.GetValue<string>("DepartmentName")));
}
Using DbContext.ChangeTracker.Entries<TEntity>
This is another option to get the DbEntityEntry for an entity. We generally use this when we are not holding on the the actual entity object. This also has its generic and non-generic versions but they are operationally different. The generic version provides DbEntityEntry objects for entities of a given type tracked by theDbContext. On the other hand the non-generic version provides them for ALL entities tracked by the system, which might not be needed most of the time. Additionally, the generic version returns the generic version of DbEntityEntry and non-generic version returns the non-generic one. In the following example, we are practically doing the same thing as the above example but we are getting the entries using DbContext.ChangeTracker. It is an IEnumerable so we can use an Iterator to play with individual values.
using (var context = new InstituteEntities())
{
var department = context.Departments.First<Department>();
department.DepartmentName = "Computer & Information Systems Engineering"; DbChangeTracker changeTracker = context.ChangeTracker;
IEnumerable<DbEntityEntry<Department>> departmentEntries =
changeTracker.Entries<Department>(); foreach (DbEntityEntry departmentEntry in departmentEntries)
{
DbPropertyValues originalDepartment = departmentEntry.OriginalValues;
DbPropertyValues updatedDepartment = departmentEntry.CurrentValues;
EntityState state = context.Entry<Department>(department).State; Console.WriteLine(
string.Format("State: {0}, Old Value: {1}, New Value: {2}",
state, originalDepartment.GetValue<string>("DepartmentName"),
updatedDepartment.GetValue<string>("DepartmentName")));
}
}
Change Tracking for Individual members of an Entity
In the above code, we saw how we can get the DbEntityEntry with current and old values. Code first lets us more fine grained interfaces through which we can capture individual properties of an entity. There are different types provided for this. All of these classes inherit from DbMemberEntry<TEntity>. It is an abstract class. All scalar properties of an entity use DbPropertyEntry. The complex properties uses futher specialized version of this and it uses DbComplexPropertyEntry.DbReferenceEntry and DbCollectionEntry are used by reference and collection based navigation properties respectively. We can present the relationship between these types as follows:

using (var context = new InstituteEntities())
{
var department = context.Departments.First<Department>();
department.DepartmentName = "Computer & Information Systems Engineering"; DbChangeTracker changeTracker = context.ChangeTracker;
IEnumerable<DbEntityEntry<Department>> departmentEntries =
changeTracker.Entries<Department>(); foreach (DbEntityEntry<Department> departmentEntry in departmentEntries)
{
DbPropertyEntry<Department, string> deparmentNameEntry =
departmentEntry.Property<string>((d) => d.DepartmentName); string oldDepartmentName = deparmentNameEntry.OriginalValue;
string modifiedDepartmentName = deparmentNameEntry.CurrentValue; EntityState state = departmentEntry.State; Console.WriteLine(
string.Format("State: {0}, Old Value: {1}, New Value: {2}",
state, oldDepartmentName,
modifiedDepartmentName));
}
}
DbEntityEntry Vs ObjectStateEntry
Before there were humans in this planet we call earth, there used to be a completely different world. There used to be a different creatures, some of them we call as Dinosaurs. But somehow they got destroyed and a new world was created. New species came into being. Even if we don't indulge into the debate of evolution Vs Intelligent design then there has been continuous evolution of human thoughts. We are learning about various hidden secrets of the universe and helping others learn. Similarly, before DbContext API, there used to be ObjectContext API. For some reason the all-knowing Creator of the framework [Microsoft] decided to get rid of the older, more complicated ObjectContext world and hence new DbContext API was born. This is definitely an intelligent design but the new species are also evolving and you see different versions like 4.2, 4.3 and so on. The problem is that they can co-exist. There has been a door to go back in time usingIObjectContextAdapter to get ObjectContext through DbContext and here the fun begins. Now we can use the features of both the world in the same space and time.ObjectContext API used to have a similar feature like DbEntityEntry, it was ObjectStateEntry. Don't be surprised if you find them in older code or see them co-exist with new DbContext API. But don't use them in any new code after you have release version of tools supporting DbContext API available unless there is any legitimate reason.
Complex Type and Change Tracking
Like lazy loading, change tracking is also not supported for complex types. This is because the complex types are not real entities. They are syntactic sugar to group a number of fields. Each member of a complex type would be created as a column in the main entity. It has no primary key. The complex type based property is itself considered for change tracking. So if we assign a new instance to a complex type reference, it is tracked automatically. Let's make changes to CourseLocation andLocationAddress to support proxy creation. [http://msdn.microsoft.com/en-us/library/dd468057.aspx]
namespace EFCodeFirstDatabaseCreation.Entities
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; public class CourseLocation
{
public virtual int CourseLocationId { get; set; }
public virtual string LocationName { get; set; }
public virtual LocationAddress Address { get; set; }
public virtual ICollection<Course> CoursesOffered { get; set; }
} public class LocationAddress
{
public virtual string StreetAddress { get; set; }
public virtual string Apartment { get; set; }
public virtual string City { get; set; }
public virtual string StateProvince { get; set; }
public virtual string ZipCode { get; set; }
}
}
Let's enable proxy creation and automatic change tracking by DbContext as follows:
public InstituteEntities()
{
this.Configuration.ProxyCreationEnabled = true;
this.Configuration.AutoDetectChangesEnabled = true;
}
Let's make some changes in the properties of a complex type for an entity and see how this affects the state of the entity itself. We can not check the state of a complex type directly using DbContext.Entries() or DbChangeTracker.Entries() as DbContext.ChangeTracker.Entries().
using (var context = new InstituteEntities())
{
var courseLocations = context.Set<CourseLocation>();
foreach (var location in courseLocations)
{
Console.WriteLine("LocationName: {0}, City: {1}",
location.LocationName, location.Address.City); location.Address.City = string.Format("Great City of {0}", location.Address.City); Console.WriteLine("Updated LocationName: {0}, City: {1}",
location.LocationName, location.Address.City);
} //Complex types not directly tracked
var trackedLocationAddressEntities = context.ChangeTracker.Entries<LocationAddress>();
Console.WriteLine("# Tracked LocationAddress: {0}", trackedLocationAddressEntities.Count()); var trackedCourseLocations = context.ChangeTracker.Entries<CourseLocation>();
Console.WriteLine("# Tracked CourseLocations: {0}", trackedCourseLocations.Count()); string courseLocationEntityStates =
string.Join(",",
trackedCourseLocations.Select(l => l.State)
.Select<EntityState,string>(n => Enum.GetName(typeof(EntityState), n))
.ToArray<string>()); Console.WriteLine("Tracked CourseLocation States: {0}", courseLocationEntityStates);
}
This would result in the following output:

Interesting...But isn't that contrary to what we just discussed? i.e. there is no automatic change tracking for complex type. Yes, that is also true. But how would we justify this output with this statement. Let me explain. Basically, the state update that we see for CourseLocation entity after updating the values of LocationAddressis not because of automatic change tracking. It is because of using DbContext.ChangeTracker.Entries to get the entity's states. It also has an implicit call toDbContext.DetectChanges() as we discussed above. So, snapshot change has been pushed which has resulted in this behavior. Let's verify this with ObjectContext API to see if that is the case. We update the same method as follows:
private static void TestMethod5()
{
using (var context = new InstituteEntities())
{
var courseLocations = context.Set<CourseLocation>();
foreach (var location in courseLocations)
{
Console.WriteLine("LocationName: {0}, City: {1}",
location.LocationName, location.Address.City); location.Address.City = string.Format("Great City of {0}", location.Address.City); Console.WriteLine("Updated LocationName: {0}, City: {1}",
location.LocationName, location.Address.City);
} Console.Write("Using ObjectContext API to get EntityState");
Console.WriteLine("*************************************");
foreach (var location in courseLocations)
{
var ocAdapter = ((IObjectContextAdapter)context).ObjectContext;
var state = ocAdapter.ObjectStateManager.GetObjectStateEntry(location).State;
Console.WriteLine("Location: {0}, City: {1}, EntityState: {2}",
location.LocationName, location.Address.City, state);
} Console.WriteLine("*************************************");
Console.Write("Using DbContext API to get EntityState");
Console.WriteLine("*************************************"); //Complex types not directly tracked
var trackedLocationAddressEntities = context.ChangeTracker.Entries<LocationAddress>();
Console.WriteLine("# Tracked LocationAddress: {0}", trackedLocationAddressEntities.Count()); var trackedCourseLocations = context.ChangeTracker.Entries<CourseLocation>();
Console.WriteLine("# Tracked CourseLocations: {0}", trackedCourseLocations.Count()); string courseLocationEntityStates =
string.Join(",",
trackedCourseLocations.Select(l => l.State)
.Select<EntityState,string>(n => Enum.GetName(typeof(EntityState), n))
.ToArray<string>()); Console.WriteLine("Tracked CourseLocation States: {0}", courseLocationEntityStates);
}
}
Now you can verify that when we get the states using ObjectContext API, it shows them as UnChanged. This is because ObjectContext API does not implicitly callDetectChanges(). But when we use DbContext API, it shows as Modified, which proves our point.

We can also use ComplexProperty method of DbEntityEntry to get the changes in complex property.
Address currentValues = context.Entry(user)
.ComplexProperty(u => u.Address)
.CurrentValue;
Download Code:
Entity Framework Code First - Change Tracking的更多相关文章
- Entity Framework Code First实体对象变动跟踪
Entity Framework Code First通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...
- Entity Framework Code First学习系列目录
Entity Framework Code First学习系列说明:开发环境为Visual Studio 2010 + Entity Framework 5.0+MS SQL Server 2012, ...
- Entity Framework Code First数据库连接
1. 安装Entity Framework 使用NuGet安装Entity Framework程序包:工具->库程序包管理器->程序包管理器控制台,执行以下语句: PM> Insta ...
- Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- Entity Framework Code First关系映射约定
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Entity Framework Code First执行SQL语句、视图及存储过程
1.Entity Framework Code First查询视图 Entity Framework Code First目前还没有特别针对View操作的方法,但对于可更新的视图,可以采用与Table ...
- Entity Framework Code First使用DbContext查询
DbContext.DbSet及DbQuery是Entity Framework Code First引入的3个新的类,其中DbContext用于保持数据库会话连接,实体变化跟踪及保存,DbSet用于 ...
- Entity Framework Code First添加修改及删除单独实体
对于一个单独实体的通常操作有3种:添加新的实体.修改实体以及删除实体. 1.添加新的实体 Entity Framework Code First添加新的实体通过调用DbSet.Add()方法来实现. ...
- 旧项目如何切换到Entity Framework Code First
Entity Framework Code First固然是好东西,然而如果是已经存在的旧有项目,如何简单方便的使用切换呢? 这里介绍一个VS的插件Entity Framework Power Too ...
随机推荐
- [hihoCoder] 第五十二周: 连通性·一
题目1 : 连通性·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢失.为了避免再 ...
- iOS数据库离线缓存思路和网络层封装
一直想总结一下关于iOS的离线数据缓存的方面的问题,然后近期也简单的对AFN进行了再次封装.全部想把这两个结合起来写一下.数据展示型的页面做离线缓存能够有更好的用户体验,用户在离线环境下仍然能够获取一 ...
- Atitit 图像处理类库 halcon11 安装与环境搭建attilax总结
Atitit 图像处理类库 halcon11 安装与环境搭建attilax总结 正常安装软件,安装前请先退出其它一切正在运行的程序. 先安装halcon-10.0-windows.exe.安装完成后 ...
- 基于node-webkit的web项目打包方案
下载node-webkit https://github.com/rogerwang/node-webkit 找到Downloads这一小节,然后下载对应平台的node-webkit预编译包.(为了介 ...
- Swift 开发中,为什么要远离 Heap?
Swift 开发中,为什么要远离 Heap? WWDC的视频 — Understanding Swift Performance 中,苹果上来就说,Heap 的操作复杂度要远远超越 Stack.所以大 ...
- 【Android】Intent解读
Intent 的作用 Intent 是一个将要执行的动作的抽象的描述,一般来说是作为参数来使用,由Intent来协助完成android各个组件之间的通讯. 比如说调用startActivity()来启 ...
- Python nose单元测试框架结合requests库进行web接口测试
[本文出自天外归云的博客园] 之前写过一篇关于nose使用方法的博客.最近在做一元乐购产品的接口测试,结合着python的requests库可以很方便的进行web接口测试并生成测试结果.接口测试脚本示 ...
- C#学习笔记(32)——委托改变窗体颜色
说明(2017-11-23 22:17:34): 1. 蒋坤的作业,点击窗体1里面的按钮,出现窗体2:点击窗体2里的按钮,窗体1改变背景色. 2. 做完窗体传值后,这个作业就很简单了. 代码: For ...
- 什么是POP3、SMTP和IMAP?
POP3 POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议.它是因特网电子邮件的第 ...
- zip伪加密
简单的话来阐述 zip伪协议的意思是说本来不需要密码的zip文件然后通过修改标志位,然后就可以达到有密码的效果对吗?但是他实际是没有密码. 一个 ZIP 文件由三个部分组成: 压缩源文件数据区+压缩源 ...