It's not the first time I run into this entity framework struggle of updating 2 objects that have a list of children with the same child objects inside that list.
A typical structure for this example would be a parent object with a list of child objects that have a tag list each. i.e:
class Parent{
public virtual ICollection<Child> Childs{get ;set;}
}
class Child {
public virtual IColletion<Tag> Tags{get; set;}
}
class Tag{
public int Id {get; set;}
public string Text {get; set;}
}
The problem being when trying to update the parent object and its children on the same run, I cannot seem to be able to find a satisfying solution to say to entity framework that the entity Tag can be duplicated.
I most of the time follow this struture to update parents with childs in entity framework. How to add/update child entities when updating a parent entity in EF
To solve this issue I used to hard code the join table Tag_To_Childs and only work with the ids of the tag object, this way entity framework doesn't run in the duplicated key issue. Anyway i would be glad to know if anybody knows a more elegant solution to this problem.
Here is how I actually update my child objects and the tag Lists
public async Task<List<Child>> updateChilds(Parent entry, Parent existingParent)
// entry being the new version and existingParent the tracked Db version
{
if (existingParent != null)
{
// Delete children
foreach (var existingChild in existingParent.Childs.ToList())
{
if (!entry.Charts.Any(c => c.ChildId== existingChild.ChildId))
_context.Childs.Remove(existingChild);
}
// Update and Insert children
foreach (var childentry in entry.Childs)
{
var existingChild = existingParent.Childs
.Where(c => c.ChildId== childentry.ChildId)
.SingleOrDefault();
if (existingChild != null)
// Update child
{
await updateChildTags(existingChild, childentry);
_context.Entry(existingChild).CurrentValues.SetValues(childentry);
}
else
{
// Insert child
var newChild = new Chart
{
Title = childentry.Title,
Order = childentry.Order
};
newChild = await updateChildTags(newChild, childentry);
existingParent.Childs.Add(newChild);
}
}
await _context.SaveChangesAsync();
}
return existingParent.Childs.ToList();
}
public async Task<Child> updateChartTypeQuestions(Child realBlock, Child entry)
{
//delete bad tags
List<Tag> toRemove = new List<Tag>();
foreach (var existingChild in realBlock.Tags.ToList())
{
if (!entry.Tags.Any(c => c.TagId== existingChild.TagId))
toRemove.Add(existingChild);
}
//add new ones
foreach (var toAdd in entry.Tags.ToList())
{
if (!realBlock.Tags.Any(c => c.TagId== toAdd.TagId))
{
Tag tag = await _context.Tags.FirstOrDefaultAsync(c => c.TagId == toAdd.TagId);
realBlock.Tags.Add(tag);
}
}
foreach (var toR in toRemove)
{
realBlock.Tags.Remove(toR);
}
//the save change is called by parent function
return realBlock;
}
The problem with this code, is if 2 childs have the same tag, only the second one will be updated correctly and the tag will be removed from the first child object.