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

前端 未结 20 1057
情书的邮戳
情书的邮戳 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:11

    You should delete old child items thisParent.ChildItems one by one manually. Entity Framework doesn't do that for you. It finally cannot decide what you want to do with the old child items - if you want to throw them away or if you want to keep and assign them to other parent entities. You must tell Entity Framework your decision. But one of these two decisions you HAVE to make since the child entities cannot live alone without a reference to any parent in the database (due to the foreign key constraint). That's basically what the exception says.

    Edit

    What I would do if child items could be added, updated and deleted:

    public void UpdateEntity(ParentItem parent)
    {
        // Load original parent including the child item collection
        var originalParent = _dbContext.ParentItems
            .Where(p => p.ID == parent.ID)
            .Include(p => p.ChildItems)
            .SingleOrDefault();
        // We assume that the parent is still in the DB and don't check for null
    
        // Update scalar properties of parent,
        // can be omitted if we don't expect changes of the scalar properties
        var parentEntry = _dbContext.Entry(originalParent);
        parentEntry.CurrentValues.SetValues(parent);
    
        foreach (var childItem in parent.ChildItems)
        {
            var originalChildItem = originalParent.ChildItems
                .Where(c => c.ID == childItem.ID && c.ID != 0)
                .SingleOrDefault();
            // Is original child item with same ID in DB?
            if (originalChildItem != null)
            {
                // Yes -> Update scalar properties of child item
                var childEntry = _dbContext.Entry(originalChildItem);
                childEntry.CurrentValues.SetValues(childItem);
            }
            else
            {
                // No -> It's a new child item -> Insert
                childItem.ID = 0;
                originalParent.ChildItems.Add(childItem);
            }
        }
    
        // Don't consider the child items we have just added above.
        // (We need to make a copy of the list by using .ToList() because
        // _dbContext.ChildItems.Remove in this loop does not only delete
        // from the context but also from the child collection. Without making
        // the copy we would modify the collection we are just interating
        // through - which is forbidden and would lead to an exception.)
        foreach (var originalChildItem in
                     originalParent.ChildItems.Where(c => c.ID != 0).ToList())
        {
            // Are there child items in the DB which are NOT in the
            // new child item collection anymore?
            if (!parent.ChildItems.Any(c => c.ID == originalChildItem.ID))
                // Yes -> It's a deleted child item -> Delete
                _dbContext.ChildItems.Remove(originalChildItem);
        }
    
        _dbContext.SaveChanges();
    }
    

    Note: This is not tested. It's assuming that the child item collection is of type ICollection. (I usually have IList and then the code looks a bit different.) I've also stripped away all repository abstractions to keep it simple.

    I don't know if that is a good solution, but I believe that some kind of hard work along these lines must be done to take care of all kinds of changes in the navigation collection. I would also be happy to see an easier way of doing it.

提交回复
热议问题