Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deep copy an entity

I found this snippet 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);
        }
    }

Which says that we can do deep copy of all related objects through this thing.

I'm trying to do a copy like this:

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

IT works, but still does not copy any nested\related records. what do I do wrong here?

I have this structure Entity->ChildEntity ->ChildChildEntity
-> - one-to-many
so I assume when I copy entity it will also copy all child records.

UPDATE: After suggestions, I did this:

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();

Getting exception on AddObject line:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

like image 207
user194076 Avatar asked Jan 26 '12 02:01

user194076


People also ask

How do you copy an entity?

To copy/paste an entity: Select the entity. Right-click the entity and click Copy.

Does clone () make a deep copy?

clone() is indeed a shallow copy. However, it's designed to throw a CloneNotSupportedException unless your object implements Cloneable . And when you implement Cloneable , you should override clone() to make it do a deep copy, by calling clone() on all fields that are themselves cloneable.

What is a clone entity?

A cloned entity is an exact copy of an entity, which means that the Transform and all other components with their set values are copied too.


3 Answers

The important point is that you must load related entities and create deep clone prior to detaching. If you detach the entity all relations are silently removed because Detach method works only for single entity and entity graph cannot consists of both attached and detached entities. That is a reason why you need serialization instead of simply calling Detach.

Don't forget to turn off lazy loading otherwise your serialization will pull data for other navigation properties from database as well. Also remember that this deep copy will create new version of all entities in the graph so adding the root entity will add all related entities as well.

like image 122
Ladislav Mrnka Avatar answered Nov 15 '22 23:11

Ladislav Mrnka


The EntityKeys for all the child objects get cloned too, so you need to set each child's EntityKey to null before trying to add them with AddObject.

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();
like image 32
Roy Goode Avatar answered Nov 16 '22 00:11

Roy Goode


If you haven't loaded the child entities before detaching the entity, they will not be serialized. Make sure all those navigational properties you want to deep clone are loaded before you detach the entity.

Edit

Eager load the navigational properties that must be serialized

var entity = db.Entities.Include("ChildEntity.ChildChildEntity")
        .Where(l=>l.ID == myId).Single();
like image 4
Eranga Avatar answered Nov 15 '22 22:11

Eranga