Self-Tracking Entities: ApplyChanges and duplicate entities

Some customers using the Self-Tracking Entities template we included in Visual Studio 2010 have ran into scenarios in which they call ApplyChanges passing a graph of entities that they put together in the client tier of their app, and then they get an exception with the following message:

AcceptChanges cannot continue because the object’s key values conflict with another object in the ObjectStateManager.

This seems to be the most common unexpected issue our customers run against when using Self-Tracking Entities. I have responded multiple times to this on email and I have meant to blog about it for some time. Somehow I was able to do it today 🙂

We believe that most people finding this exception are either calling ApplyChanges to the same context with multiple unrelated graphs or they are merging graphs obtained in multiple requests so that they now have duplicate entities in the graph they pass to ApplyChanges.

By duplicate entities, what I mean is that they have more than one instance of the same entity, in other words, two or more objects with the same entity key values.

The current version of Self-Tracking Entities was specifically designed to not handle duplications. In fact, when we were designing this, our understanding was that a situation like this was most likely the result of a programming error and therefore it was more helpful to our customers to throw an exception.

The problem with the exception is that avoiding introducing duplicate entities can be hard. As an example, let’s say that you have service that exposes three service operations for a car catalog:

GetModelsWithMakes: returns a list of car Models with their respective associated Makes

GetMakes: returns the full list of car Makes

UpdateModels: takes a list of car Models and uses ApplyChanges and SaveChanges to save changes in the database

And the typical operation of the application goes likes this:

Your client application invokes GetModelsWithMakes and uses it to populate a grid in the UI.

Then, the app invokes GetMakes and uses the results to populate items in a drop down field in the grid.

When a Make “A” is selected for a car Model, there is some piece of code that assigns the instance of Make “A” to the Model.Make navigation property.

When changes are saved, the UpdateModels operation is called on the server with the graph resulting from the steps above.

This is going to be a problem if there was another Model in the list that was already associated with the same Make “A”: since you brought some Makes with the graph of Models and some Makes from a separate call, you now have two completely different instances of “A” in the graph. The call to ApplyChanges will fail on the server with the exception describing a key conflict.

There are changes we have considered doing in the future to the code in ApplyChanges in order to avoid the exception but in the general case there might be inconsistencies between the states of the two Make “A” instances, and they can be associated with a different Models, making it very difficult for ApplyChanges to decide how to proceed.

In general, the best way to handle duplicates in the object graph seems to be to avoid introducing them in the first place!

Here are a few patterns that you can use to avoid them:

1. Only use Foreign Key values to manipulate associations:

You can use foreign key properties to set associations between objects without really connecting the two graphs. Every time you would do something like this:

model.Make = make;

… replace it with this:

model.MakeId = make.Id;

This is the simplest solution I can think of and should work well unless you have many-to-many associations or other “independent associations” in your graph, which don’t expose foreign key properties in the entities.

2. Use a “graph container” object and have a single “Get” service operation for each “Update” operation:

If we combine the operations used to obtain car Models and Makes into a single service operation, we can use Entity Framework to perform “identity resolution” on the entities obtained, so that we get a single instance for each make and model from the beginning.

This is a simplified version of “GetCarsCatalog” that brings together the data of both Models and Makes.

This approach should work well even if you have associations without FKs. If you have many-to-many associations, it will be necessary to use the Include method in some queries, so that the data about the association itself is loaded from the database.

3. Perform identity resolution on the client:

If the simple solutions above don’t work for you, you can still make sure you don’t add duplicate objects in your graph while on the client.The basic idea for this approach is that every time you are going to assign a Make to a Model, you pass the Make through a process that will help you find whether there is already another instance that represents the same Make in the graph of the Model, so that you can avoid the duplication.

This is a really complicated way of doing it compared with the two solutions above, but using Jeff’s graph iterator template, it doesn’t really take a lot of extra code to do it:

Notice that the last argument of the MergeWith method is a delegate that is used to compare key values on instances of the TEntity type. When using EF, you can normally take for granted that EF will know what properties are the keys and that identity resolution will just happen automatically, but since on the client-side you only have a graph of Self-Tracking Entities, you need to provide this additional information.

Summary

Some customers using Self-Tracking Entities are running into exceptions in cases in which duplicate entities are introduced, typically when they have entities retrieved in multiple service operations merged into a single graph. ApplyChanges wasn’t designed to handle duplicates and the best practice is to avoid introducing the duplicates in the first place. I have showed a few patterns that can help with that.

Personally, I believe the best compromise between simplicity and flexibility is provided by a combination of the first and second patterns. For instance, you can use only foreign keys properties to associate entities with reference/read-only data (e.g. associate an OrderLine with a Product in an application used to process Orders), and use graph containers to transfer data that can be modified in the same transaction, i.e. entities that belong in the same aggregate (e.g, Order and its associated OrderLines).

Hi Diego, I´d like to use DDD aproach on ASP.NET WebForms App, so I have my Poco Classes in separate assembli of the .edmx Model, I´m serializing the My POCO classes in ViewState and it works fine to Insert and Retrive data from my Repository, also the LazyLoading with the Virtual in my List<T> properties works fine. But, to persist in Data Base the updated object graph is another long story. I´ve been looking for one easy answer to that but I did´t find yet, belive me I´ve been reading a lot about EF. This Aproach with STE looks to be more apropriate to use with WCF stuff I´m not using services on my App. I was trying to use the STE generator but my first problem was It´s generated in the same assemblie of .edmx Model and if I move that to my Domain assemblie to try to use partial Classes in My domain I lost the Interfaces IChangeble…… ITracklebel….

Can you give me any tip just to Save back my object Graph with the modifications, the Funny part is The modifications are there in the object But the modifications (add or delete ) of my lists are ignored by the EF4.

I am running into the problem described in the post and was hoping to come up with a pure server side solution because it is very difficult to control the all the use cases on the client. The solution I had at first was to override Equals and GetHashCode to compare on entity keys, and while it worked great when one entity was persisted at any given time, the solution broke down when ApplyChanges was called on multiple entities sequentially.

I'm facing this problem and never received any response to my numerous posts on the web. I do not understand why having 2 entities with the same EntityKey is a problem if those entities are in an unchanged state. STE is unusable in a real world app. I've a lot of many-to-many tables and thus, cannot bind to ID as stated in your first point. The second one is a performance-killer, i do not want to download a complete list of Make with each Model, just to be able to select one. Concerning the third point, it's not an example for a many-to-many table. Waht about a merge for TrackableCollection<T>?

I'm completely stuck on a project using Silverlight / WCF and EF 4 STE. Can you please tell me how to proceed to save the entity back to the database?

…am I correct in assuming that the default behaviour of STEs would be that if one sets the FK property (as noted in *2) then at that point the corresponding object property (which is model.Make in this case) would be invalid, etiher null or pointing to some object that is not necessarily correct AND, I further assume, that once that modified "model" entity does get back to the Context and ApplyChanges() is called and SaveChanges() is called then (and only then) will the object property (model.Make) be set properly and ready to consume and use?

Hi, I have found an issue with the Iterator solution to this problem. In almost all cases it works but in this one case its still an issue and the iterator solution is the only solution we can use.

Lets say I have two Person properties and a find person screen.

I find a person and set it to person 1 using the merge. I find a person and set it to person 2 using the merge and save. This works correctly.

I go back into it and i find the same person set on person 2 and set it to person 1, I find the same person that was previously in person 1 and set it to person 2.

What this causes is two copies of the person that was originally in person 1. The reason for this is when i set person 1 to person 2 it properly finds the merge and uses it, but then what was in person 1 originally is now in OriginalValues. When i set person 2 to what was originally in person 1 the iterator does not find it because it exists in OriginalValues as an old value.

When apply changes is called it still hits this issue because two copies exist of the same object, one directly in a navigation property and another in original values.

Is there any way to fix this? This issue overall is causing some major headaches. I dont see why it is so hard to have the concept of finding a record and bringing it into an object graph to save the key. We have to have the actual objects for modification purposes and display purposes. there are other ways we can solve this in the UI layer but we really want to solve this in the model layer.

I ran into this problem a lot and found that it could also be resolved by overriding the Equals method of each entity class to compare the key values. Reading the above, are you saying that this should not have worked?

Just an additional note on only using the foreign key ID: I needed to be able to attach the same Product to PurchaseDetailLines and was getting the error, but my grid view uses the Product.Name property. Since everthing related to products was read-only I was able to do a foreach loop through the detail lines and set the Product entity equal to null. The foreign key kept and I was still able to use the referenced objects before the save.

Hi guys This is very interesting… However, I'm getting that exception too but in a different scenario I guess..

I have already a recond In DB/DataContext.

I deleted the record from DB and detach it from EntityContext.

Then a second user comes, executes same logic, detects that the record no longer exist but needs it, so it is created again..

All this happens in two separated threads and both start at the same time (A user clicking OK to same Form for example).

When I accept all changes for the record that is being created I get that Exception. Seems like the one I already deleted somehow still remains in memory/cache.. so, when the second thread tries to created again that record and acept the changes it conflicts with the other one.