实体框架6深度复制/克隆具有动态深度的实体

Luk*_*uke 7 .net c# linq entity-framework entity-framework-6

我正在尝试深度克隆/复制包含相同类型的子项目的实体项目.Item也有参数,也应该克隆.但是,ItemType应保留为对现有ItemType的引用.

插图图: 在此输入图像描述

在Stackoverflow(实体框架5深度复制/克隆实体)的帮助下,我提出了以下相当糟糕的尝试:

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;
}
Run Code Online (Sandbox Code Playgroud)

对于固定的深度等级3,这种尝试就像一个魅力.但是,正如您所看到的,对于每个更深层次而言,这并不是很好.该设计允许无限数量的嵌套(但在我的上下文中,不应超过5个级别).

是否有可能根据最大深度动态添加包含到IQueryable?

这是要克隆的Item-entity:

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; }
}
Run Code Online (Sandbox Code Playgroud)

Luk*_*uke 4

我找到了解决这个问题的更通用的方法。对于可能遇到类似问题的人,我现在解决的方法如下:

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;
}
Run Code Online (Sandbox Code Playgroud)

请记住,递归调用必须在 EntityState.Added 分配之前。否则,递归将在第二层停止。此外,必须使用处于附加状态的实体来调用递归方法。否则,递归也会在第二层停止。

如果您的实体树非常深,请考虑用迭代方法替换递归。有关更多信息,请查看:维基百科递归与迭代

欢迎反馈和改进!