The relationship could not be changed because one or more of the foreign-key properties is non-nullable

前端 未结 20 1449
情书的邮戳
情书的邮戳 2020-11-22 04:44

I am getting this error when I GetById() on an entity and then set the collection of child entities to my new list which comes from the MVC view.

The

20条回答
  •  情书的邮戳
    2020-11-22 05:09

    This is a very big problem. What actually happens in your code is this:

    • You load Parent from the database and get an attached entity
    • You replace its child collection with new collection of detached children
    • You save changes but during this operation all children are considered as added becasue EF didn't know about them till this time. So EF tries to set null to foreign key of old children and insert all new children => duplicate rows.

    Now the solution really depends on what you want to do and how would you like to do it?

    If you are using ASP.NET MVC you can try to use UpdateModel or TryUpdateModel.

    If you want just update existing children manually, you can simply do something like:

    foreach (var child in modifiedParent.ChildItems)
    {
        context.Childs.Attach(child); 
        context.Entry(child).State = EntityState.Modified;
    }
    
    context.SaveChanges();
    

    Attaching is actually not needed (setting the state to Modified will also attach the entity) but I like it because it makes the process more obvious.

    If you want to modify existing, delete existing and insert new childs you must do something like:

    var parent = context.Parents.GetById(1); // Make sure that childs are loaded as well
    foreach(var child in modifiedParent.ChildItems)
    {
        var attachedChild = FindChild(parent, child.Id);
        if (attachedChild != null)
        {
            // Existing child - apply new values
            context.Entry(attachedChild).CurrentValues.SetValues(child);
        }
        else
        {
            // New child
            // Don't insert original object. It will attach whole detached graph
            parent.ChildItems.Add(child.Clone());
        }
    }
    
    // Now you must delete all entities present in parent.ChildItems but missing
    // in modifiedParent.ChildItems
    // ToList should make copy of the collection because we can't modify collection
    // iterated by foreach
    foreach(var child in parent.ChildItems.ToList())
    {
        var detachedChild = FindChild(modifiedParent, child.Id);
        if (detachedChild == null)
        {
            parent.ChildItems.Remove(child);
            context.Childs.Remove(child); 
        }
    }
    
    context.SaveChanges();
    

提交回复
热议问题