In the article Change tracking with POCOs
, we have seen what is meant by change tracking, why should we track changes in POCO entities and ways of change tracking mechanisms with POCOs. In this article we will how to perform Snapshot Change Tracking using DetectChanges() method and its pros and cons.
We know that unlike classes inherited from EntityObject, POCO classes cannot communicate with the ObjectContext. This means that POCO entities cannot notify its changes to state manager. So, it is the responsibility of ObjectContext to synchronize the POCO data with ObjectStateEntry objects.
To synchronize the POCO data with ObjectStateEntry objects, DetectChanges() method of ObjectContext is used. When DetectChanges() method is called, ObjectContext detects the changes made to POCO entities by taking a snapshot of current state of all the entities. Note that the ObjectContext has a snap shot of entity values when the query is executed for the first time, to retrieve data from the database. The ObjectContext maintains two sets of values. The first set contains original values and the second one contains current values. We can say that the ObjectContext detects the changes made to POCO entities by comparing original and current values of all the entities and constructs the various commands to persist the changes back to the database.
By default, the SaveChanges method calls DetectChanges() method internally and we need not call it explicitly. This type of change tracking is done without creating proxies. At runtime, POCO will be in pure state without a wrapper of the proxy class. This approach is the choice of many programmers because of its simplicity. The other available mechanism - Change Tracking with proxies creates problems while serializing the objects in enterprise applications and so it is not preferred.
How Snapshot Change Tracking works
As we have seen earlier, whenever we call SaveChanges() method, synchronization happens but not immediately. Sometimes situation demands that changes to properties and relationships are detected immediately and in those cases, we should call the DetectChanges() explicitly.
The disadvantage in this mechanism is that POCO entities cannot notify its changes automatically. Unless we call DetectChanges or SaveChanges() method is called only then synchronization happens. After entities are attached to the context for the first time retrieval, they are synchronized but after any change like adding a new entity or modifying any one scalar property, they both are out of synchronization.
The DetectChanges() method iterates the entities and checks each property for changes. Iteration can be wasteful if lots of entities are to be checked.
I will demonstrate How Snapshot Change Tracking works with an example, given below. In the code given, we set the ProxyCreationEnabled property to false. And we create a POCO class as pure POCO class.
All the properties are not declared with virtual keyword and this class is pure POCO class without proxies.
When we query the ObjectStateManager class, it returns the ObjectStateEntry object(s) tracked by the ObjectContext. The GetObjectStateEntry() method of ObjectStateManager class returns an ObjectStateEntry() object for a given entity. The GetObjectStateEntries() method returns a collection of ObjectStateEntry objects for a given state. The ObjectStateEntry class also exposes a number of properties such as the State property which returns the state of the entity. In the above example, we query the first customer and display the entity´s state. The entity state is displayed as Unchanged. Note that when entities are attached to the context, their state is Unchanged by default.
If we change the customer name and again display the state of the entity, surprisingly we get the state as Unchanged even though we have modified the name. But if you see the database the changes are persisted. How does this happen? In the earlier part of this article, we have discussed that the SaveChanges() method internally calls the DetectChanges() method and this method checks all the entries in the ObjectStateManager and compares all the properties of all the entities and if there is any change in the properties, that entity state will be changed to Modified. This is the reason the state is returned as Unchanged. At this point, the values of entities and the entries in the state manager are synchronized. After the synchronization process, the SaveChanges method persists the data to the database.
If we want to synchronize POCO entities and state manager entries before calling SaveChanges() method, we need to explicitly call DetectChanges() method.