如何深度复制实体

时间:2012-01-26 02:33:53

标签: c# entity-framework .net-4.0

我找到了这个代码段here

public static T DeepClone<T>(this T obj)
    {
        using (var ms = new MemoryStream()) {
            var bf = new BinaryFormatter();
            bf.Serialize(ms, obj);
            ms.Position = 0;
            return (T)bf.Deserialize(ms);
        }
    }

其中说我们可以通过这个东西对所有相关对象进行深层复制。

我正在尝试这样的副本:

db.Detach(myEntity); 
myEntity.EntityKEy = null;
Entity newEntity = new Entity();
newEntity = DeepClone<Entity>(Entity);
db.Entities.AddObject(newEntity);
db.SaveChanges();

IT可以工作,但仍然不会复制任何嵌套的\相关记录。我在这做错了什么?

我有这个结构Entity-&gt; ChildEntity - &gt; ChildChildEntity
- &GT; - 一对多
所以我假设当我复制实体时它也会复制所有子记录。

更新 建议后,我这样做了:

Entity newEntity = new Entity();
Eneity Entity = db.Include("ChildEntity").Where(p=>p.Id==Id).Single();
newEntity = DeepClone<Entity>(Entity);
db.Detach(myEntity); 
myEntity.EntityKEy = null;
db.Entities.AddObject(newEntity);
db.SaveChanges();

在AddObject行上获取异常:

  

ObjectStateManager中已存在具有相同键的对象。   ObjectStateManager无法跟踪具有相同对象的多个对象   键。

4 个答案:

答案 0 :(得分:7)

重要的一点是,您必须加载相关实体并在分离之前创建深度克隆。如果分离实体,则会以静默方式删除所有关系,因为Detach方法仅适用于单个实体,而实体图形不能同时包含附加实体和分离实体。这就是为什么需要序列化而不是简单地调用Detach

的原因

不要忘记关闭延迟加载,否则您的序列化也会从数据库中提取其他导航属性的数据。还要记住,这个深层副本将在图中创建所有实体的新版本,因此添加根实体也将添加所有相关实体。

答案 1 :(得分:5)

所有子对象的EntityKeys也被克隆,因此在尝试使用AddObject添加它们之前,需要将每个子对象的EntityKey设置为null。

Entity oldEntity = db.Include("ChildEntity").Where(p => p.Id == Id).Single();
Entity newEntity = oldEntity.DeepClone(); // assuming you've put your DeepClone extension method in a static class so that it can be used as an extension
newEntity.EntityKey = null;
foreach(var childEntity in newEntity.ChildEntities)
{
    childEntity.EntityKey = null;
}
db.Entities.AddObject(newEntity);
db.SaveChanges();

答案 2 :(得分:4)

如果在分离实体之前尚未加载子实体,则不会序列化它们。确保在分离实体之前加载了要深度克隆的所有导航属性。

修改

急切加载必须序列化的导航属性

var entity = db.Entities.Include("ChildEntity.ChildChildEntity")
        .Where(l=>l.ID == myId).Single();

答案 3 :(得分:2)

您可能应该在尝试再次附加实体之前保存上下文

Entity newEntity = new Entity();   
Eneity Entity = db.Include("ChildEntity").Where(p=>p.Id==Id).Single();   
newEntity = DeepClone<Entity>(Entity);   
db.Detach(myEntity);    
db.SaveChanges();  
myEntity.EntityKEy = null;   
db.Entities.AddObject(newEntity);   
db.SaveChanges();