Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attaching an entity of type 'X' failed because another entity of the same type

I've stumbled upon a strange bug in my code. Which was working before, but now works sometimes.

I am using EF6 to Edit an entity with some relations. To not edit the relations I 'Attach' them (see example code).

public void EditA(A ThisIsA, B ThisIsB)
    {
        using (var Context = new LDZ_DEVEntities())
        {
            Context.As.Attach(ThisIsA);

            var b = Context.Bs.FirstOrDefault(x => x.BId == ThisIsB.BId);
            //var b = Context.Bs.Find(ThisIsB.BId);

            if (b != null)
                Context.Bs.Attach(b);
            else
                b = ThisIsB;

            if (b.C != null)
                Context.Cs.Attach(b.C);

            ThisIsA.Bs.Add(b);

            Context.SaveChanges();

        }
    }

I've edited the names to keep it simple.

The following line

Context.Cs.Attach(b.C);

throws this error:

Attaching an entity of type 'C' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

This line was introduced because all the C entities are static entities. I never want a C to be created. If I remove this line, every time when I will add a B to A; a C is created. Which is not desirable.

Extra info:
A has a list of B's
B has one C

This EditA() Method is being called at multiple places in my software. This error only appears when the method is called in a loop (import). There are no problems while working on the first record. But I’m getting the error in the records after the first one.

I've read this questions plus answers but they weren't working for me:

  1. ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value

  2. Attaching an entity of type failed because another entity of the same type already has the same primary key value

like image 411
Robin Gordijn Avatar asked May 20 '15 12:05

Robin Gordijn


2 Answers

I fixed it.

In Fabio Luz his answer, he said:

//if A has been loaded from context
//dont attach it
//if it has been created outside of the context
//Context.Entry(ThisIsA).State = EntityState.Modified;

This got me thinking, so I edited my code to this:

public void EditA(A ThisIsA, B ThisIsB)
{
    using (var Context = new LDZ_DEVEntities())
    {
        var a = Context.As.Find(ThisIsA.AId);

        //var b = Context.Bs.FirstOrDefault(x => x.BId == ThisIsB.BId);
        var b = Context.Bs.Find(ThisIsB.BId);

        if (b != null)
            Context.Bs.Attach(b);
        else
            b = ThisIsB;

        if (b.C != null)
            Context.Cs.Attach(b.C);

        a.Bs.Add(b);

        Context.SaveChanges();

    }
}

Summary of changes:

  • Changed FirstOrDefault to Find
  • Get A from Context

At first I removed the Attach of C, as a result this created a new entity. So I reversed this change.

Special thanks to Fabio Luz. I couldn't have done this without your help!

like image 181
Robin Gordijn Avatar answered Nov 05 '22 09:11

Robin Gordijn


Take a look at the following link https://msdn.microsoft.com/en-us/data/jj592676.aspx

If you have an entity that you know already exists in the database but to which changes may have been made then you can tell the context to attach the entity and set its state to Modified. For example:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" }; 

using (var context = new BloggingContext()) 
{ 
    context.Entry(existingBlog).State = EntityState.Modified; 

    // Do some more work...  

    context.SaveChanges(); 
}

NOTE: you don't have to do this with all objects (A, B and C), just with A.

EDIT 1

Based on your comment, try this:

//check if 
var _b = Context.Bs.Find(ThisIsB.BId);

if (_b != null)
  //b doesn't exist, then add to the context
  //make sure that the primary key of A is set.
  //_b.PrimaryKeyOfA = someValue;
  Context.Bs.Add(_b);
else
 //b already exists, then modify the properties
 //make sure that the primary key of A is set.

Context.SaveChanges();

EDIT 2

I didn't tested but it should work.

public void EditA(A ThisIsA, B ThisIsB)
{
    using (var Context = new LDZ_DEVEntities())
    {
        //if A has been loaded from context
        //dont attach it
        //if it has been created outside of the context
        //Context.Entry(ThisIsA).State = EntityState.Modified;

        var _b = Context.Bs.Find(ThisIsB.BId);

        if (_b == null)
        { 
            _b = ThisIsB;
        }

        ThisIsA.Bs.Add(_b);

        Context.SaveChanges();

    }
}
like image 7
Fabio Luz Avatar answered Nov 05 '22 09:11

Fabio Luz