2015-11-23 4 views
6

Я пытаюсь глубоко клонировать/копировать сущность Элемент, который содержит дочерние элементы того же типа. Элемент также имеет параметры, которые также должны быть клонированы. Однако ItemType следует оставить ссылкой на существующий ItemType.Entity Framework 6 глубокая копия/клонирование объекта с динамической глубиной

Схема для иллюстрации: enter image description here

С помощью Stackoverflow (Entity Framework 5 deep copy/clone of an entity) я придумал следующую довольно паршивую попытку:

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

Для фиксированной глубины уровня 3 этой попытки работает как шарм. Однако, как вы можете видеть, это не очень приятно с каждым более глубоким уровнем. Конструкция допускает бесконечное количество вложенности (в моем контексте, однако, не должно быть более 5 уровней).

Есть ли возможность динамически добавлять Включает в IQueryable в зависимости от максимальной глубины?

Это Позиционный-объект для клонирования:

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

ответ

2

Я нашел более общий подход к этой проблеме. Для тех, кто может столкнуться с подобной проблемой, вот как я ее решил сейчас:

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

Имейте в виду, что рекурсивный вызов должен быть перед выделением EntityState.Added. В противном случае рекурсия остановится на втором уровне. Кроме того, рекурсивный метод должен быть вызван с Entity в состоянии Attached. В противном случае рекурсия остановится и на втором уровне.

Рассмотрите возможность замены рекурсии на итеративный подход, если ваше дерево сущности очень глубокое. Для получения дополнительной информации смотрите: Wikipedia Recursion versus iteration

Отзывы и улучшения приветствуются!