Entity Helpers
protected void UpdateChildCollection<Tparent, Tid , Tchild>(Tparent dbItem, Tparent newItem, Func<Tparent, IEnumerable<Tchild>> selector, Func<Tchild, Tid> idSelector) where Tchild : class
{
var dbItems = selector(dbItem).ToList();
var newItems = selector(newItem).ToList();
if (dbItems == null && newItems == null)
return;
var original = dbItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();
var updated = newItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();
var toRemove = original.Where(i => !updated.ContainsKey(i.Key)).ToArray();
var removed = toRemove.Select(i => DbContext.Entry(i.Value).State = EntityState.Deleted).ToArray();
var toUpdate = original.Where(i => updated.ContainsKey(i.Key)).ToList();
toUpdate.ForEach(i => DbContext.Entry(i.Value).CurrentValues.SetValues(updated[i.Key]));
var toAdd = updated.Where(i => !original.ContainsKey(i.Key)).ToList();
toAdd.ForEach(i => DbContext.Set<Tchild>().Add(i.Value));
}
/*
which you can call with something like:
UpdateChildCollection(dbCopy, detached, p => p.MyCollectionProp, collectionItem => collectionItem.Id)
Unfortunately, this kinda falls over if there are collection properties on the child type which also need to be updated. Considering trying to solve this by passing an IRepository (with basic CRUD methods) which would be responsible for calling UpdateChildCollection on its own. Would call the repo instead of direct calls to DbContext.Entry.
Have no idea how this will all perform at scale, but not sure what else to do with this problem.
*/
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
// Retrieve the error messages as a list of strings.
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
// Join the list to a single string.
var fullErrorMessage = string.Join("; ", errorMessages);
// Combine the original exception message with the new one.
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}