Self Tracking Entities - AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager
Asked Answered
M

5

12

I've been stuck with this problem for over a week now. Hopefully some one can point me in the right direction.

I start with a brief description of my schema.

Asset 1--->1 Address *-->1 Area *-->1 Region *-->1 Country

Package 1-->* Asset

Using Self Tracking Entity (STE) + WCF.

Steps:

  1. Call data store for a list of assets.
  2. Call data store for a list of packages.
  3. User selects a package and assign some assets to it.
  4. Save package.

In step 2, the call uses eager loading of Addresses.

from p in context.Assets.Include("Address.Area.Region.Country")

This is the error when attempting to call

context.Packages.ApplyChanges(package)

AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.

EDIT

After snooping around, i found that this is a STE issue. The problem being you cannot persist a graph that contains multiple instances of the same entity as outlined here. Here is my question.

How can I add an entity to my entity collection. The new entity may have related entities that contain the same key as one already in the collection. I.e. Add a new asset that may contain the same address, area, region or country entity.

Here are my constrains:

  1. I must use the navigational collection because it affect the UI.
  2. I cannot pre-fetch all entities that will be involved because the data set is simply too large.
  3. I must be able to take snap-shots of the entity at anytime in order to keep a history and use it to "undo" any changes.

I am aware of the possible solutions suggested by Diego B Vega, but those are not options i can use for my solution. Has anyone any other ideas?

Maybe answered 28/7, 2010 at 6:19 Comment(2)
You have duplicate keys, which isn't allowed. That's all I can say without having your code.Stu
I think you need to post a minimalistic code example where the error occurs. It's hardly possible to say anything rather than you have duplicate keys from the info provided in your post.Scathing
C
8

FYI, I wrote a blog post with some additional suggestions to what I had already responded in the EF Forums. Here is the blog post.

Chessa answered 6/10, 2010 at 16:52 Comment(2)
This is a very useful blog. Just what i needed. The graph iterator is worth looking into.Maybe
It would be nice if EF would add the name of the offending objects/keys to is long error messages. For someone maintaining an existing application, a small change in the code may cause inadvertently a whole new object graph to be created. Precise error messages are invaluable. Thanks for you blog.Mountie
B
8

Have you considered just giving up on ORM-s and going back to normal access, if you know what I mean :-)

Not kidding - by the time you wrestle with one single issue like that one (which smells like ORM bug more than anything else) you could have rolled out your own 5-10 functions to do normal sproc calls and easier data type conversion and then you are back to being in full control and and not stuck by libraries which are going to take another like 5yr to stabilize.

Especially since you seem to have very clean schema - meaning quite simple queries and straight forward updates.

Beryl answered 5/8, 2010 at 12:18 Comment(3)
I really like the thought of that but unfortunately i don't have quite as much flexibility as that. I will put it into consideration in future project designs.Maybe
I just dismantled one, like 2 mo ago so it's from the personal experience :-) Nedded just one simple class to create/cleanup connection and excecue readers (it's DataReader 99% of the times anyway) and one static class to construct In/Out params on the fly (6-8 types) and convert nullable results (6-8 types again) with short functions (so I can type less :-) Oh and one smart template class to auto-dispose reader and connection. What a relief - not to mention perf boost :-)Beryl
the problem is managing those "5-10 functions" for every feature of the site. ORM is more than saved time up front. it's not having to make a change in 5 places every time you add/change a propertyJannelle
C
8

FYI, I wrote a blog post with some additional suggestions to what I had already responded in the EF Forums. Here is the blog post.

Chessa answered 6/10, 2010 at 16:52 Comment(2)
This is a very useful blog. Just what i needed. The graph iterator is worth looking into.Maybe
It would be nice if EF would add the name of the offending objects/keys to is long error messages. For someone maintaining an existing application, a small change in the code may cause inadvertently a whole new object graph to be created. Precise error messages are invaluable. Thanks for you blog.Mountie
A
3

I encountered the same issue and finally came up with a solution. The basic idea is to prevent certain navigational class type from attaching to the ObjectContext. Here is what I did:

  1. Modified the context.tt template to make SelfTrackingEntitiesContextExtensions class partial.
  2. Copy the 2 ApplyChanges functions to newly created Custom.Context.Extension.cs and rename them as CustomApplyChanges. Each will have one additional parameter as array of Type

public static void CustomApplyChanges(this ObjectContext context, string entitySetName, TEntity entity, Type[] excludeTypes) where TEntity : IObjectWithChangeTracker

  1. Add a condition to the for loop to exclude any class type contains in the excludeTypes array.

region Handle Initial Entity State

foreach (IObjectWithChangeTracker changedEntity in entityIndex.AllEntities.Where(x => x.ChangeTracker.State == ObjectState.Deleted && !excludeTypes.Contains(x.GetType()))) { HandleDeletedEntity(context, entityIndex, allRelationships, changedEntity); }

foreach (IObjectWithChangeTracker changedEntity in entityIndex.AllEntities.Where(x => x.ChangeTracker.State != ObjectState.Deleted && !excludeTypes.Contains(x.GetType()))) { HandleEntity(context, entityIndex, allRelationships, changedEntity); }

endregion

  1. The usage

Type[] excludeTypes = { typeof(Asset), typeof(Address), typeof(Region)};

rep.Entities.CustomApplyChanges(entity, excludeTypes); var changedEntry = rep.Context.ObjectStateManager.GetObjectStateEntries>(System.Data.EntityState.Added | System.Data.EntityState.Modified | System.Data.EntityState.Deleted); foreach (var e in changedEntry) { if (excludeTypes.Any(c => c == e.Entity.GetType())) { rep.Context.Detach(e.Entity); //detach unchanged objects } }

Aegir answered 13/8, 2010 at 18:46 Comment(2)
I've used this method with great success. Thank you for sharing.Classroom
I have an Entity that has 2 associations to another entity and get the error message in the original post. I have implemented this. It appears that this code is just detaching the Exclude types, so these don't get saved at all. I don't get the error but the associations (Where the Ids are set) don't get saved? Not sure how this is going to work? Have I missed something?Cooley
C
1

I solved this by Setting the resetting the Foreign Key Ids, which required setting the Navigation value to null, before saving.

...something like this:

var countryToId = address.CountryToId;
var countryFromId = address.CountryFromId;
documentAddress.CountryTo = null;
documentAddress.CountryFrom = null;
documentAddress.CountryToId = countryToId;
documentAddress.CountryFromId = countryFromId;
Cooley answered 11/5, 2013 at 13:14 Comment(0)
H
1

I was getting this error because I was deleting the records of an entity, reseeding it and then refilling the entity with new data.

As self tracking was enabled on this entity, it did not allow adding the record with the same key, even though a record with that key had been deleted earlier.

Since I did not need self tracking in this situation, I disabled it as:

dbcontext.entity.MergeOption = System.Data.Objects.MergeOption.NoTracking;
Hans answered 22/4, 2014 at 11:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.