2015-11-23 6 views
6

Sto tentando di clonare/copiare in profondità un oggetto che contiene elementi figlio dello stesso tipo. L'articolo ha anche parametri, che dovrebbero essere clonati. Il ItemType, tuttavia, deve essere lasciato come riferimento al ItemType esistente.Entity Framework 6 deep copy/clone di un'entità con profondità dinamica

Schema illustrativo: enter image description here

Con l'aiuto di StackOverflow (Entity Framework 5 deep copy/clone of an entity) Ho venire con il seguente tentativo piuttosto scadente:

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

Per una profondità livello fisso di 3 questo tentativo funziona come un fascino. Tuttavia, come puoi vedere, questo non sta diventando molto carino ad ogni livello più profondo. Il design consente un numero infinito di nidificazione (nel mio contesto, tuttavia, non dovrebbero esserci più di 5 livelli).

C'è qualche possibilità di aggiungere dinamicamente Include a IQueryable a seconda della profondità massima?

Questo è l'elemento-entità per clonare:

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

risposta

2

ho trovato un approccio più generico per questo problema. Per chi potrebbe incontrare un problema simile, ecco come ho risolto per ora:

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

Tenete a mente che la chiamata ricorsiva deve essere prima della allocazione EntityState.Added. Altrimenti, la ricorsione si fermerà al secondo livello. Inoltre, il metodo ricorsivo deve essere chiamato con un'entità in uno stato Attached. Altrimenti, la ricorsione si fermerà anche al secondo livello.

Considerare di sostituire la ricorsione con un approccio iterativo se l'albero delle entità è molto profondo. Per ulteriori informazioni, dai un'occhiata a: Wikipedia Recursion versus iteration

Feedback e miglioramenti benvenuti!