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

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

    Using the solution of Slauma, I created some generic functions to help update child objects and collections of child objects.

    All my persistent objects implement this interface

    /// 
    /// Base interface for all persisted entries
    /// 
    public interface IBase
    {
        /// 
        /// The Id
        /// 
        int Id { get; set; }
    }
    

    With this I implemented these two functions in my Repository

        /// 
        /// Check if orgEntry is set update it's values, otherwise add it
        /// 
        /// The collection
        /// The entry
        /// The original entry found in the database (can be null is this is a new entry)
        /// The added or updated entry
        public T AddOrUpdateEntry(DbSet set, T entry, T orgEntry) where T : class, IBase
        {
            if (entry.Id == 0 || orgEntry == null)
            {
                entry.Id = 0;
                return set.Add(entry);
            }
            else
            {
                Context.Entry(orgEntry).CurrentValues.SetValues(entry);
                return orgEntry;
            }
        }
    
        /// 
        /// check if each entry of the new list was in the orginal list, if found, update it, if not found add it
        /// all entries found in the orignal list that are not in the new list are removed
        /// 
        /// The type of entry
        /// The database set
        /// The new list
        /// The original list
        public void AddOrUpdateCollection(DbSet set, ICollection newList, ICollection orgList) where T : class, IBase
        {
            // attach or update all entries in the new list
            foreach (T entry in newList)
            {
                // Find out if we had the entry already in the list
                var orgEntry = orgList.SingleOrDefault(e => e.Id != 0 && e.Id == entry.Id);
    
                AddOrUpdateEntry(set, entry, orgEntry);
            }
    
            // Remove all entries from the original list that are no longer in the new list
            foreach (T orgEntry in orgList.Where(e => e.Id != 0).ToList())
            {
                if (!newList.Any(e => e.Id == orgEntry.Id))
                {
                    set.Remove(orgEntry);
                }
            }
        }
    

    To use it i do the following:

    var originalParent = _dbContext.ParentItems
        .Where(p => p.Id == parent.Id)
        .Include(p => p.ChildItems)
        .Include(p => p.ChildItems2)
        .SingleOrDefault();
    
    // Add the parent (including collections) to the context or update it's values (except the collections)
    originalParent = AddOrUpdateEntry(_dbContext.ParentItems, parent, originalParent);
    
    // Update each collection
    AddOrUpdateCollection(_dbContext.ChildItems, parent.ChildItems, orgiginalParent.ChildItems);
    AddOrUpdateCollection(_dbContext.ChildItems2, parent.ChildItems2, orgiginalParent.ChildItems2);
    

    Hope this helps


    EXTRA: You could also make a seperate DbContextExtentions (or your own context inferface) class:

    public static void DbContextExtentions {
        /// 
        /// Check if orgEntry is set update it's values, otherwise add it
        /// 
        /// The context object
        /// The collection
        /// The entry
        /// The original entry found in the database (can be null is this is a new entry)
        /// The added or updated entry
        public static T AddOrUpdateEntry(this DbContext _dbContext, DbSet set, T entry, T orgEntry) where T : class, IBase
        {
            if (entry.IsNew || orgEntry == null) // New or not found in context
            {
                entry.Id = 0;
                return set.Add(entry);
            }
            else
            {
                _dbContext.Entry(orgEntry).CurrentValues.SetValues(entry);
                return orgEntry;
            }
        }
    
        /// 
        /// check if each entry of the new list was in the orginal list, if found, update it, if not found add it
        /// all entries found in the orignal list that are not in the new list are removed
        /// 
        /// The type of entry
        /// The context object
        /// The database set
        /// The new list
        /// The original list
        public static void AddOrUpdateCollection(this DbContext _dbContext, DbSet set, ICollection newList, ICollection orgList) where T : class, IBase
        {
            // attach or update all entries in the new list
            foreach (T entry in newList)
            {
                // Find out if we had the entry already in the list
                var orgEntry = orgList.SingleOrDefault(e => e.Id != 0 && e.Id == entry.Id);
    
                AddOrUpdateEntry(_dbContext, set, entry, orgEntry);
            }
    
            // Remove all entries from the original list that are no longer in the new list
            foreach (T orgEntry in orgList.Where(e => e.Id != 0).ToList())
            {
                if (!newList.Any(e => e.Id == orgEntry.Id))
                {
                    set.Remove(orgEntry);
                }
            }
        }
    }
    

    and use it like:

    var originalParent = _dbContext.ParentItems
        .Where(p => p.Id == parent.Id)
        .Include(p => p.ChildItems)
        .Include(p => p.ChildItems2)
        .SingleOrDefault();
    
    // Add the parent (including collections) to the context or update it's values (except the collections)
    originalParent = _dbContext.AddOrUpdateEntry(_dbContext.ParentItems, parent, originalParent);
    
    // Update each collection
    _dbContext.AddOrUpdateCollection(_dbContext.ChildItems, parent.ChildItems, orgiginalParent.ChildItems);
    _dbContext.AddOrUpdateCollection(_dbContext.ChildItems2, parent.ChildItems2, orgiginalParent.ChildItems2);
    

提交回复
热议问题