RSinohara RSinohara - 1 month ago 12
C# Question

Entity framework attach: object with same key exists

I am building a windows form application, and I use multiple DBContext instances (mostly one per Business layer call).

After literally days of dealing with an issue (while inserting new entities, the ones they referred to were added as new ones, instead of using the existing entities), I found out that the problem was I had to attach the existing entities to the context.

All was good for about 2 hours, when I then got errors while attaching: the entity with the same key exists in the context.

I tried testing before attaching (similar method for every entity type):

private void attachIfNeeded(POCO.Program myObject, myContext context)
{
if (!context.Set<POCO.Program>().Local.Any(e => e.ID == myObject.ID))
{
context.programs.Attach(myObject);
return true;
}
else
{
myObject = context.Set<POCO.Program>().Local.Single(e => e.ID == myObject.ID);
return false;
}
}


But the tests return false, but it still fails when attaching.

So basically, if I don't attach, it will add a new entity instead of using the existing (and intended) one. If I do attach, there's an error I can't figure out.

I have looked around (doing this the whole day now) and I actually (think I) know what the problem is:

The entity I am trying to add has multiple relationships, and other entities can be reached by multiple paths. Could that cause the problem?

Please help with this, solutions out there really make no sense to me and haven't worked.

I am really close to the point where I will try-catch around the attach statement and be done with it. But I will hate doing it.

Here are my entities (not all of them, but this should be enough):

public class Word
{
[Key]
public int ID {get;set;}

[Required]
public string word { get; set; }


public WordCategories category { get; set; }

public Word parent {get;set;}

public List<Unit> units { get; set; }

public Program program { get; set; }

public List<Lesson> lessons { get; set; }

public Word()
{
units = new List<Unit>();
lessons = new List<Lesson>();
}
}
public class Unit
{
[Key ]
public int ID { get; set; }

[Required]
public string name { get; set; }

public string description { get; set; }

public List<Lesson> lessons { get; set; }

public Program program {get;set;}

public List<Word> words { get; set; }

public Unit()
{
lessons=new List<Lesson>();
words = new List<Word>();
}

}


And here is where I am calling the attach method. The error is thrown on the first attach:

public int addWords(List<POCO.Word > words,int programID, int unitID,int lessonID)
{
CourseHelperDBContext context = getcontext();

int result;



foreach(POCO.Word a in words)
{
foreach (POCO.Unit b in a.units)
attachIfNeeded(b, context);
foreach(POCO.Lesson c in a.lessons )
attachIfNeeded(c,context);
attachIfNeeded(a.program,context);
if (a.parent != null)
attachIfNeeded(a.parent,context);
}

context.words.AddRange(words);
result = context.SaveChanges();
return result;

}


I cannot believe I'm having so many issues with this. I just want to store those entities, add some (I haven't gotten to the point where I would change them) and save it.

So far I've figured:


  1. Some words are new, some exist and some are changed (mostly parent property);

  2. All units exist, as do programs and lessons (so I need to attach them);

  3. The object graph contains multiple paths to entities, some of which exist, some of which are new;

  4. I am using a new context for every request. I run into other issues when I was using the same all the time. I found solutions that pointed to this pattern, and I think it's OK since that's what you'd do on an ASP MVC project.



All these could be causing problems, but I don't know which and how to work around them.

I think I can make this work by adding one word at a time, and pulling programs, lessons and units every time... but that means many many round trips to the DB. This can't be the way.

Answer

Back to this after quite some time, the problem in this case was that I needed to retrieve the entities that were present on my relationships.

The solution was neither attach (because it would fail if the entity is already attached) nor add (because it already existed on the DB).

What I should have done was to retrieve every entity related to the one I was adding.

This helped: Entity Framework creating new entity with relationship to existing entity, results in attempt to create new copy of the existing entity