Entity Framework 6 deep copy / clone object with dynamic depth

I am trying to deeply clone / copy an Item object that contains children of the same type. The item also has parameters that must also be cloned. However, an ItemType should be left with a reference to an existing ItemType.

Diagram for illustration: enter image description here

Using Stackoverflow ( deep copy / clone Entity Framework 5 object). I came up with the following rather nasty attempt:

public Item DeepCloneItem(Item item) { Item itemClone = db.Items //Level 1 .Include(i => i.ChildrenItems.Select(c => c.ChildrenItems )) //3 Levels .Include(i => i.Parameters) //Level 1 Params .Include(i => i.ChildrenItems.Select(c => c.Parameters)) //Level 2 Params .Include(i => i.ChildrenItems.Select(c => c.ChildrenItems .Select(cc => cc.Parameters))) //Level 3 Params .AsNoTracking() .FirstOrDefault(i => i.ItemID == item.ItemID); db.Items.Add(itemClone); db.SaveChanges(); return itemClone; } 

With a fixed depth level of 3, this attempt works like a charm. However, as you can see, this is not very pleasant with every deeper level. The design allows an infinite number of nested ones (in my context, however, there should not be more than 5 levels).

Is it possible to dynamically add Includes to IQueryable depending on the maximum depth?

This is an Item object to clone:

 public class Item { public int ItemID { get; set; } public int? ParentItemID { get; set; } [ForeignKey("ParentItemID")] public virtual Item ParentItem { get; set; } public virtual ICollection<Item> ChildrenItems { get; set; } [InverseProperty("Item")] public virtual ICollection<Parameter> Parameters { get; set; } public ItemTypeIds ItemTypeID { get; set; } [ForeignKey("ItemTypeID")] public virtual ItemType ItemType { get; set; } } 
+6
source share
1 answer

I found a more general approach for this problem. For those who may run into a similar problem, here is how I solved it now:

 public Item DeepCloneItem(Item item) { Item itemClone = db.Items.FirstOrDefault(i => i.ItemID == item.ItemID); deepClone(itemClone); db.SaveChanges(); return itemClone; } private void deepClone(Item itemClone) { foreach (Item child in itemClone.ChildrenItems) { deepClone(child); } foreach(Parameter param in itemClone.Parameters) { db.Entry(param).State = EntityState.Added; } db.Entry(itemClone).State = EntityState.Added; } 

Keep in mind that a recursive call must be prior to allocating EntityState.Added. Otherwise, recursion will stop at the second level. In addition, the recursive method must be called with Entity in the Attached state. Otherwise, recursion will also stop at the second level.

Consider replacing recursion with an iterative approach if your entity tree is very deep. For more information, see: Wikipedia Recursion vs. Iteration

Feedback and improvements are welcome!

+2
source

All Articles