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
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);