Entity Framework 6 深度复制/克隆具有动态深度的实体

Posted

技术标签:

【中文标题】Entity Framework 6 深度复制/克隆具有动态深度的实体【英文标题】:Entity Framework 6 deep copy/clone of an entity with dynamic depth 【发布时间】:2016-02-25 19:23:54 【问题描述】:

我正在尝试深度克隆/复制包含相同类型子项的实体项。该项目也有参数,也应该被克隆。然而,ItemType 应该作为对现有 ItemType 的引用。

图解:

在 *** (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 层)。

是否有可能根据最大深度将 Includes 动态添加到 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; 

【问题讨论】:

【参考方案1】:

我找到了一种更通用的方法来解决这个问题。 对于可能遇到类似问题的任何人,这是我现在解决的方法:

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 分配之前。否则,递归将停止在第二级。 此外,递归方法必须调用处于 Attached 状态的实体。否则,递归也将停止在第二层。

如果您的实体树非常深,请考虑用迭代方法替换递归。欲了解更多信息,请查看:Wikipedia Recursion versus iteration

欢迎反馈和改进!

【讨论】:

以上是关于Entity Framework 6 深度复制/克隆具有动态深度的实体的主要内容,如果未能解决你的问题,请参考以下文章

.NET Entity Framework 与复制的 SQL 服务器一起使用

Entity Framework 6 暂时禁用拦截

Entity Framework Core 6.0 预览4 性能改进

Entity Framework 6 开发系列 目录

添加 [DataContract] 到 Entity Framework 6.0 POCO Template

在Oracle中使用Entity Framework 6 CodeFirst