1

I am trying to remove an object and some other child objects using EF. Here's the method I am using:

public async Task<IHttpActionResult> Delete([FromBody]WordForm wordForm)
{
    var olddObj = db.WordDefinitions
    .Where(w => w.WordFormId == wordForm.WordFormId)
    .AsNoTracking()
    .ToList();
    foreach (var wordDefinition in olddObj)
    {
        db.WordDefinitions.Attach(wordDefinition);
        db.WordDefinitions.Remove(wordDefinition);
    }
    db.WordForms.Attach(wordForm);
    db.WordForms.Remove(wordForm);
    await db.SaveChangesAsync();
    return Ok();
}

Can anyone explain to me why I might be getting this message:

Attaching an entity of type 'Entities.Models.Core.WordDefinition' 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.

Here's the object definition:

public class WordForm 
{
    public string WordFormId { get; set; } // WordFormId (Primary key) (length: 20)
    public int WordFormIdentity { get; set; } // WordFormIdentity

    // Reverse navigation
    public virtual System.Collections.Generic.ICollection<WordDefinition> WordDefinitions { get; set; } // WordDefinition.FK_WordDefinitionWordForm

    public WordForm()
    {
        WordDefinitions = new System.Collections.Generic.List<WordDefinition>();
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
Samantha J T Star
  • 30,952
  • 84
  • 245
  • 427
  • The first part of your method is already loading the objects into the graph so there is no need to attach them before removing. just remove them. the second attach should work fine, but if not you would also be better of loading it into the graph as well before removing it. – Nkosi May 18 '16 at 16:47
  • Thank you, I will try that now and update your answer. – Samantha J T Star May 18 '16 at 16:52
  • You might have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:36

2 Answers2

2

I know this may not be your desired solution but you can set 'Delete rule' in database to 'Cascade' and whenever you delete an object , all of its child will be deleted.

Kahbazi
  • 14,331
  • 3
  • 45
  • 76
2

The first part of your method is already loading the objects into the graph when they are returned from the query, so there is no need to attach them. Just remove the already loaded objects. The second attach should work fine, but it would also work to load it into the graph using its primary key and then removing it.

Here is a refactored version of your method...

public async Task<IHttpActionResult> Delete([FromBody]WordForm wordForm) {
    if(wordForm == null) return BadRequest();
    var toRemove = db.WordForms.FirstOrDefault(w => w.WordFormId == wordForm.WordFormId);
    if(toRemove != null) {
        var olddObj = db.WordDefinitions
        .Where(w => w.WordFormId == wordForm.WordFormId)
        .ToList();
        //removing children
        foreach (var wordDefinition in olddObj) {
            db.WordDefinitions.Remove(wordDefinition);
        }
        //remove parent object
        db.WordForms.Remove(toRemove);
        await db.SaveChangesAsync();
        return Ok();
    }
    return NotFound();
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472