Cloning data on Entity Framework
Entity FrameworkEntity Framework Problem Overview
I am creating software where user can create new product based on older product.
Now I need to make copying / cloning operations with Entity Framework. First I started writing like this:
foreach(sourcedata1 in table1)
{
... create new table
... copy data
... create Guid
... add
foreach(sourcedata2 in table2)
{
... create new table
... copy data
... create Guid
... add
... and so on
}
}
Problem is that this not a nice way to do it. Is there any easy way clone information (except Guid that needs to be generated for new rows) or should I manually copy everything?
Other solution
You could also use EmitMapper or AutoMapper to do copying of the properties.
Entity Framework Solutions
Solution 1 - Entity Framework
To clone an Entity in Entity Framework you could simply Detach the entity from the DataContext
and then re-add it to the EntityCollection
.
context.Detach(entity);
entityCollection.Add(entity);
Update for EF6+ (from comments)
context.Entry(entity).State = EntityState.Detached;
entity.id = 0;
entity.property = value;
context.Entry(entity).State = EntityState.Added;
context.SaveChanges();
Solution 2 - Entity Framework
public static EntityObject Clone(this EntityObject Entity)
{
var Type = Entity.GetType();
var Clone = Activator.CreateInstance(Type);
foreach (var Property in Type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.SetProperty))
{
if (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(EntityReference<>)) continue;
if (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>)) continue;
if (Property.PropertyType.IsSubclassOf(typeof(EntityObject))) continue;
if (Property.CanWrite)
{
Property.SetValue(Clone, Property.GetValue(Entity, null), null);
}
}
return (EntityObject)Clone;
}
This is a simple method I wrote. It should work for most people.
Solution 3 - Entity Framework
To add a new row whose content is based on an existing row, follow these steps:
- Get an entity based on the starting row.
- Set the entry state for the entity to Added.
- Modify the entity.
- Save changes.
Here's an example:
var rabbit = db.Rabbits.First(r => r.Name == "Hopper");
db.Entry(rabbit).State = EntityState.Added;
rabbit.IsFlop = false;
db.SaveChanges();
Solution 4 - Entity Framework
If you want to create a copy of an entity for comparison later in your code execution, you can select the entity in a new db context.
If for example you are updating an entity, then later in the code you want to compare the updated and original entity:
var db = new dbEntityContext();
var dbOrig = new dbEntityContext();
var myEntity = db.tblData.FirstOrDefault(t => t.Id == 123);
var myEntityOrig = dbOrig.tblData.FirstOrDefault(t => t.Id == 123);
//Update the entity with changes
myEntity.FirstName = "Gary";
//Save Changes
db.SaveChnages();
At this point, myEntity.FirstName
will contain "Gary"
whilst myEntityOrig.FirstName
will contain the original value. Useful if you have a function to log changes where you can pass in the updated and original entity.
Solution 5 - Entity Framework
A really short way of duplicating entities using generics (VB, sorry).
It copies foreign key values (external IDs) but doesn't load their related objects.
<Extension> _
Public Function DuplicateEntity(Of T As {New, Class})(ctx As myContext, ent As T) As T
Dim other As New T 'T is a proxy type, but New T creates a non proxy instance
ctx.Entry(other).State = EntityState.Added 'attaches it to ctx
ctx.Entry(other).CurrentValues.SetValues(ent) 'copies primitive properties
Return other
End Function
For instance:
newDad = ctx.DuplicateEntity(oDad)
newDad.RIDGrandpa ' int value copied
newDad.Grandpa ' object for RIDGrandpa above, equals Nothing(null)
newDad.Children ' nothing, empty
I don't know exactly how to reload Grandpa
in this case.
This doesn't work:
ctx.SaveChanges()
ctx.Entry(newDad).Reload()
but really, no problem. I would rather assign Grandpa
by hand if I need it.
newDad.Grandpa = oDad.Grandpa
EDIT: As MattW proposes in his comment, detaching and finding the new entity you get its children loaded (not collections).
ctx.Entry(newDad).State = EntityState.Detached
ctx.Find(newDad.RowId) 'you have to know the key name