Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity framework attach: object with same key exists

Tags:

c#

.net

entity

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.

like image 304
RSinohara Avatar asked Oct 31 '25 22:10

RSinohara


2 Answers

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

like image 123
RSinohara Avatar answered Nov 03 '25 12:11

RSinohara


After attaching the entity, try setting the entity state to modified.

context.programs.Attach(myObject);
context.Entry(myObject).State = EntityState.Modified;

I think there's a mistake in your test logic.

If entity does not exist in database, you should be adding instead of attaching. Your code is attaching if it can't find an entity when it should really be adding.

Code to add a new entity (Create/Insert)

context.Set<T>.Add(entity);

Code to attach an entity (Update)

context.Set<T>.Attach(entity);
context.Entry(entity).State = EntityState.Modified;

If your code is failing on the first attach, that would be attachIfNeeded(b,context); ? I don't think you have shown us the code for this.

like image 24
failedprogramming Avatar answered Nov 03 '25 12:11

failedprogramming



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!