The information in this post is out of date.
Visit msdn.com/data/ef for the latest information on current and past releases of EF.
In my last post on POCO, I mentioned the fact that there are two types of change tracking possibilities with POCO: Snapshot based Change Tracking and Notification based Change Tracking with Proxies. In this post, I would like to drill into both options a bit further and cover the advantages and disadvantages of both, along with the implications of using either approach. We will also be posting ideas on the EF Design Blog regarding some of the feedback we have received so far about POCO support in Entity Framework 4.0.
Snapshot based Change Tracking (pure POCO without proxies)
As I mentioned in Part 2 of this series, Snapshot based change tracking is what you get with pure POCO entities that don’t use proxies to handle change tracking. This is a simple change tracking solution that relies on complete snapshots of before and after values being maintained by the Entity Framework. These values are compared during SaveChanges to determine what has truly changed from their original values. In this model, unless you are using Lazy loading, the runtime type of your entities is the same as you define for your POCO entities.
There really isn’t anything problematic with this approach, and if you care about the runtime purity of your entity types without the use of proxies, it is perfectly fine to rely on this approach and take the route of pure POCO entities that don’t rely on proxy types for additional functionality.
The only catch with Snapshot based change tracking is that there are a few things you have to be aware of: because there is no direct notification of changes to the Entity Framework anytime your objects change, Entity Framework’s Object State manager will go out of sync with your own object graph.
Let’s take an example to see how this manifests itself:
customer = (
from
c in context.Customers
where c.CustomerID == "ALFKI"
select
c).Single();
ObjectStateEntry
ose = context.ObjectStateManager.GetObjectStateEntry(customer);
Console
.WriteLine("Customer object state: {0}", ose.State); // Unchanged
customer.Country = "UK";
Console
.WriteLine("Customer object state: {0}", ose.State); // Still Unchanged
In this example, Customer is a pure POCO type. Unlike with EntityObject or IPOCO based entities, making changes to the entity doesn’t automatically keep the state manager in sync because there is no automatic notification between your pure POCO entities and the Entity Framework. Therefore, upon querying the state manager, it thinks that the customer object state is Unchanged even though we have explicitly made a change to one of the properties on the entity.
If you were to perform SaveChanges without choosing to acceptChangesDuringSave, you will see that the state becomes Modified after the save. This is because the snapshot based change tracking kicked in during save and detected the change. Of course, the default SaveChanges call will leave the state back to Unchanged since the default Save behavior is to accept changes during save.
We’ll discuss more about what to do when you would actually need to keep things in sync between your objects, the object graph and the state manager in a little bit. But first let’s look at the other type of change tracking that’s available to you with POCO.
Notification based Change Tracking with Proxies
This is a different solution if you care about very efficient and up to date change tracking as you make changes to your entity values, relationships and object graph: Proxy based Change Tracking. You can leverage proxy based change tracking for a particular entity type if you declare all mapped properties on that entity type as virtual.
Entities that are tracked using change tracking proxies will always be in sync with the Entity Framework’s Object state manager because the proxies will notify the Entity Framework of the changes to values and relationships as each change happens. On the whole, this makes it more efficient to track changes because the object state manager can skip comparing original and current values of properties that it knows haven’t changed.
For all intents and purposes, the change tracking behavior you get with proxies is identical to the change tracking behavior you get with EntityObject based non-POCO entities or IPOCO entities.
Let’s take the same example:
customer = (
from
c in context.Customers
where c.CustomerID == "ALFKI"
select
c).Single();
ObjectStateEntry
ose = context.ObjectStateManager.GetObjectStateEntry(customer);
Console
.WriteLine("Customer object state: {0}", ose.State); // Unchanged
customer.Country = "UK";
Console
.WriteLine("Customer object state: {0}", ose.State); // Modified
The example is self explanatory – Object State Manager was notified of the changes as you made changes to your entity. There is no additional overhead you have to incur during SaveChanges.
Proxy based change tracking does however mean that the runtime type of your entities is not exactly the same type you defined – rather, it is a subclass of your own type. This can be undesirable in many cases (such as serialization) and you have to choose the approach that best works within the constraints and the requirements of your application and domain.
Keeping the State Manager in Sync without using Proxies
There are pros and cons to both approaches and it certainly won’t be surprising that the simplicity and elegance of non proxy based pure POCO will mean that it becomes the default choice for many of you. So let’s look at how you can keep the state manager in sync with your object graph when you work in this mode.
ObjectContext.DetectChanges()
There is one method that matters the most when you work with snapshot based pure POCO entities: ObjectContext.DetectChanges().
This API should be used explicitly whenever you have made changes to your object graph, and it will inform the state manager that it needs to sync up with your graph.
ObjectContext.SaveChanges will implicitly invoke DetectChanges by default, so you don’t need to explicitly call DetectChanges if all you are doing is making a series of changes to your objects and immediately performing a Save. However, keep in mind that depending on the size of your graph, DetectChanges may turn out to be expensive (also DetectChanges might be unnecessary depending on what you are doing) and therefore it is possible for you to skip DetectChanges that happens implicitly during SaveChanges – more on this when we discuss SaveChanges additions that are new to Entity Framework 4.0.
Let’s take a look at how DetectChanges impacts our example we looked at earlier:
customer = (
from
c in context.Customers
where c.CustomerID == "ALFKI"
select
c).Single();
ObjectStateEntry ose = context.ObjectStateManager.GetObjectStateEntry(customer);
Console
.WriteLine("Customer object state: {0}", ose.State); // Unchanged
customer.Country = "UK";
Console
.WriteLine("Customer object state: {0}", ose.State); // Still Unchanged
context.DetectChanges();
Console
.WriteLine("Customer object state: {0}", ose.State); // Modified
Calling DetectChanges explicitly brings the state manager in sync with the state of your objects.
Because DetectChanges is potentially expensive, it isn’t called explicitly during the other APIs on ObjectContext that rely on the state manager being in sync with the object graph. As a result, you have to call DetectChanges whenever you are performing state dependent operations on the context.
The behavior of the following APIs in Object Servces depend on the current state in the ObjectStateManager and therefore could be affected if knowledge aobut the part of the graph on which they operate is out of sync with the actual state of the graph:
ObjectContext API:
- AddObject
- Attach
- AttachTo
- DeleteObject
- Detach
- GetObjectByKey
- TryGetObjectByKey
- ApplyCurrentValues
- ApplyPropertyChanges
- ApplyOriginalValues
- Refresh
- ExecuteQuery
ObjectStateManager API:
- ChangeObjectState
- ChangeRelationshipState
- GetObjectStateEntry
- TryGetObjectStateEntry
- GetObjectStateEntries
ObjectStateEntry API:
- Any
EntityCollection/EntityReference API:
- Any
New Overloads for SaveChanges
In Entity Framework 3.5, there were two possibilities when invoking ObjectContext.SaveChanges():
- SaveChanges() – The parameterless option allowed you to save changes, and also accept changes implicitly
- SaveChanges(bool acceptChangesDuringSave) – This option allowed you to opt out of the default behavior of “accept changes during save”. You will have to explicitly use AcceptAllChanges to accept changes after the save.
With DetectChanges behavior added to the mix, it was obvious that adding additional overloads would clutter the API. In order to address this problem, the SaveOptions flags enum was introduced:
[
Flags
]
public enum
SaveOptions
{
None = 0,
DetectChangesBeforeSave = 1,
AcceptChangesAfterSave = 2,
}
SaveChanges now includes an overload that looks like this:
public virtual int SaveChanges(
SaveOptions
options);
SaveOptions will allow us (the Entity Framework team) to extend the API in a much more manageable way should we need to add additional options in the future.
But this overload is also interesting for another reason: Note that this overload of SaveChanges is virtual. In Entity Framework 4.0, you can override the SaveChanges behavior with your own behavior customization, as a part of the ObjectContext derived implementations that you might choose to write. Whether or not you are using POCO support in EF, this will allow you add customizations in a way that was not possible with Entity Framework 3.5.
That pretty much wraps up most of the things I wanted to highlight about POCO in Entity Framework 4.0. Stay tuned for a write-up on using Patterns with Entity Framework next.
Faisal Mohamood
Program Manager, Entity Framework Team
PingBack from http://aspmvc.co.cc/2009/06/10/poco-in-the-entity-framework-part-3-%e2%80%93-change-tracking-with-poco/
It is always busy here with all the improvements we are doing in Entity Framework to make your code work
I have a model where POCOs have all properties and methods marked as virtual and the classes have a public default constructor. They inherit from the base Entity class that overrides ToString, Equals and GetHashCode object methods and define a couple of abstract properties. The context has lazy loading and proxy change tracking enabled. The objects have a proxy type at runtime.
I still have to execute DetectChanges() in order for the object state to become modified. Am I missing something?
Dmitry,
In general, the answer is no.
As Faisal explains, the need for the DetectChanges() API comes from the fact that Entity Framework supports plain old CLR objects that are not proxies and therefore do not notify the ObjectStateManager of property changes.
To be even more explicit, you will never need to invoke DetectChanges(), as long as you know that the totality of the entity instances that are being tracked are POCO Proxy instances.
Entity Framework can track changes on graphs made of all the different POCO “kinds” below:
1. POCO Proxies
2. Non-Proxy POCOs
3. POCOs that conform with the Proxy requirements but are actually Non-Proxy instances
Even if your class complies with all the requirement for proxies, you only get #1 when you either
a. Retrieve entities from a query
b. Invoke the new ObjectContext.CreateObject<T>() API.
Otherwise, you end up with #3.
Now, only when the graph of objects you are tracking contains instances of #2 or #3, there are changes that the knowledge in ObjectStateManager won’t stay in sync with the latest changes in the graph.
Then, the behavior of the APIs that Faisal lists in this post depends on the information on the ObjectStateManager, and therefore, there are chances that you might get unexpected results unless you invoke DetectChanges() *before* invoking those APIs.
There are many cases however, in which even if you don’t invoke DetectChanges, things are going to be just fine. This is the case, for instance, if the part of the graph the method needs to reason about is different from the part of the graph previous steps in the program have manipulated.
Finally, SaveChanges() implicitly invokes DetectChanges(). As a result, invoking DetectChanges() explicitly is unnecessary in many simple cases.
Hope this answers the question,
Diego
Diego,
My issue is I do not seem to be getting POCO proxies. I do get them from a query. I believe I tested this behavior by using GetObjectByKey method as well as LINQ to Entity queries.
I implement transaction auditing in the SavingChanges event handler and it would be nice to know the object state before calling DetectChanges/SaveChanges.
I ended up calling DetectChanges in the beginning of the event handler and then call the overload of SaveChanges that does not invoke DetectChanges. This way I don’t have to manually track objects created with the "new" operator.
Hello Dmitry,
Creating a class that is “proxy-able” doesn’t per se provide you proxies. If you use simple instance construction like in
var p = new Product();
… you will get non-proxies. If you need free proxy instances, you have to do something like this:
Var p = context.CreateObject<Product>();
If you are still getting non-proxies after doing this, a common cause is that the collection navigation properties in the type are not of type ICollection<T>. This is also a requirement for change tracking proxies.
Regarding your second point, in SaveChanges, by default, DetectChanges is invoked right before raising the SavingChanges event, so you shouldn’t need to call DetectChanges yourself.
If for some reason you want to inspect the state of the ObjectStateManager before DetectChanges (just becasue I am not sure I understood the question correctly), I see two easy alternatives:
1. Override SaveChanges and do the inspection before calling base.SaveChanges
2. Invoke SaveChanges with ~SaveOptions.DetectChangesBeforeSave
Hope this helps,
Diego
Thank you for submitting this cool story – Trackback from DotNetShoutout
Hello,
I think the new functionality in EF 4.0 addresses very well some on the limitations of the previous version.
I have some questions, and I hope someone can help me find their answers. I am working on a 3-tiered application, and I would like to use ChangeTracking with POCOs. My questions are:
1. Can the Proxies be serialized by WCF?
2. If Proxies can be serialize, how can it be done?
3. What type should I specify in the signature of the WCF methods?
Thanks,
Guilgamesh
Daily tech links for .net and related technologies – June 11, 2009 Web Development Opinionated Input
Hello Sanjeev,
I don’t know how to interpret this…
Guilgamesh
Is it possible to get the associated ObjectContext from an EntityObject.
Will the next release (.NET 4??) of Entity Framework provide this in a more fundamental fashion.
Maybe as a property like EntityObject.ObjectContext?
9efish.感谢你的文章 – Trackback from 9eFish
I agree with Pagal, having access to the active ObjectContext from within an EntityObject would be extremely helpful.
If you have been watching this blog, you know that I have been discussing the various aspects of POCO
If you have been watching this blog, you know that I have been discussing the various aspects of POCO
Hi
I like to define interfaces for my entities (IProduct and ICategory). That allows me for easy decoration, replacement, proxying, mocking.
Do you support working with interfaces instead of concrete classes? This way you support lazy-loading and change tracking without having to use virtuals.
Thanks
@Guilgamesh
Proxies cannot be directly serialized with WCF serialization, but there are ways to serialize these entities where the proxy is transformed into a "pure poco" (non-proxy). You wouldn’t necessarily want to serialize the proxies with WCF because then the proxy definition would have to be part of your OperationContract. This would tightly couple your client code with your persistence layer and you’d loose some of the goodness you got from using POCO in the first place. What we will have is an attribute you can add to your operation contract that uses a new WCF feature called a DataContractResolver. This attribute will signal to WCF to serialize proxy instances as their base, non-proxy DataContract.
When using pure-POCO classes on the client, you will loose the ability to do change tracking. If your goal is: how do I do change tracking of POCOs accross tiers, I’d look into a solution that uses self-tracking entities, which are still POCO in that they do not have a dependency on the persistence framework, but do have code inside them that knows how to track and report changes. There are other blog posts about self-tracking entity support with the Entity Framework on this blog.
Jeff
@Sly
We do not support mapping to interfaces yet. This request is coming up more and more frequently, though as it does allow some interesting patterns (and proxying).
If we supported interfaces and you wanted EF to do lazy loading/change tracking for you, would your assumption be that we just generate our own implementation of the entire class? How would you inject business logic into the class in this case?
Thanks for the note!
Jeff
@ Jeff: Hi, thanks for your answer.
I would code a concrete Product class with all my business logic. The Product class would implement the IProduct interface that I would also define.
I would expect that you decorate (GoF decorator pattern) the IProduct interface to add lazy loading/change tracking. Thus, your code would end-up forwarding all calls to my real product implementation where my business logic is. But you would not know about the Product class, just the IProduct interface.
Thanks
Sly
I agree with Pagal, having access to the active ObjectContext from within an EntityObject would be extremely helpful.
@Dmitry, just to clarify: when I previously said that DetectChanges is invoked immediately before the SavingChanges event is raised I actually got it upside down. It happens after, to make sure it captures any change you do in your graph inside the event handler. If you need to use the ObjectStateManager API to iterat through entities, you should invoke DetectChanges yourself inside the SavingChanges handler as you mentioned.
Does anybody know how to save an entity object in different context? Here is my code.
NorthwindPocoDatabaseTests t = new NorthwindPocoDatabaseTests();
Product product = t.getProduct("Chai");
t.Finish();
t.Start();
product.UnitPrice = 99;
t.saveProduct(product);
t.Finish();
….
public bool saveProduct(Product p)
{
context.Products.Attach(p);
context.SaveChanges();
return false;
}
Hi Faisal,
The EF team has done a terrific job of enlisting community feedback and integrating it into the product. POCO in EF 4 is exactly what I would have hoped for. Clean and simple with the magic completely abstracted away and unobtrusive. Keep up the good work!
FYI, the EF proxies give me the heebs, but I love the pure POCO. 🙂
– Jason
@Jeff & Sly
Regarding interfaces: I’m fine with using concrete classes for the ObjectSet<T> and CreateObjectSet<T>, but would like to use them for navigation properties e.g. as in:
Order.Product : IProduct
This works fine when reading from the DB, but throws an error when I’m trying to set or update the property.
Isn’t it possible to use interfaces for my properties in the POCO objects?
(The reasons among others are testability, abstraction etc.)
Best regards, Jakob
stackoverflow.com/…/ef-4-0-dynamic-proxies-poco-object-does-not-match-target-type
I have a problem where old records are of dynamic proxy type and new records are the poco class, afer an insert into the database. This causes Databinding to fail because .net thinks I have diffrent objects in my enumberable. I posted the detail and some code at the stack overflow link above. I think i am doing something wrong with changetracking and the POCO's but I'm not sure exactly what.
does the POCO in EF4.0 save changes to oracle?
thx
Lyu.KingT#gmail.com
Thanks, great post. Couple of questions:
If your POCO classes needs to be virtual for domain reasons i.e. your POCO class meets the requirements for having a proxy, but you don't want the Proxy classes to be spun up, then is there a way to stop them ?
Also I have seen this pattern where people have called DetectChanges, and follow up with a call to SaveChanges. As SaveChanges will call DetectChanges implicitly, therefore is this introducing unnecessary processing / time?
Thanks
Mickey
Hi, great article.
I have a question about reverting changes that's made to an entity!
I have a data grid, and it’s ItemsSource is bound to a DbSet, and its SelectedItem is bound to a property in view model.
If selected item is not null, user can edit it in a new window. as user changes the values in the text boxes, those changes are reflected to the data grid as well.
Noooooow! when i undo changes as you described, property values get changed and I can see them in Quick Watch, but values of text boxes stay the same!
Any suggestions to solve this prob?
Thanks in advance
@Vahid – To get changes to be reflected in your text boxes when you update properties from code you would need to implement INotifyPropertyChanged (so that the data binding framework gets notified when it needs to refresh the text box).
This is a great article – simple enough to understand; comprehensive enough to enlighten. I've recently ready many articles but all fade away into jargon and assumptions about the reader's knowledge. Given I am coming to this subject almost 5 years from it's inception it was great to read an article that is still relevant and without a great deal of divergence. Also the samples worked "out of the zipfile!"