Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy Entity Framework Object

I have a EF4.1 class X and I want to make copy of that plus all its child records. X.Y and X.Y.Z

Now if I do the following it returns error.

The property 'X.ID' is part of the object's key information and cannot be modified.

public void CopyX(long ID)
{
    var c = db.Xs.Include("Y").Include("W").Include("Y.Z").SingleOrDefault(x => x.ID == ID);
    if (c != null)
    {
        c.ID = 0;
        c.Title = "Copy Of " + c.Title;
        for (var m = 0; m < c.Ys.Count; m++)
        {
            c.Ys[m].ID = 0;
            c.Ys[m].XID=0-m;
            for (var p = 0; p < c.Ys[m].Zs.Count; p++)
            {
                c.Ys[m].Zs[p].XID = 0 - m;
                c.Ys[m].Zs[p].ID = 0 - p;
            }
        }
        for (var i = 0; i < c.Ws.Count; i++)
        {
            c.Ws[i].ID = 0 - i;
            c.Ws[i].XID = 0;
        }
        db.Entry<Content>(c).State = System.Data.EntityState.Added;
        db.SaveChanges();
    }
}

Or Is there other way of making copy of entity objects.

NOTE: there are multiple properties in each W,X,Y,Z.

like image 640
Anuj Pandey Avatar asked Aug 24 '11 08:08

Anuj Pandey


2 Answers

In entity-framework-5, this is insanely easy with the DbExtensions.AsNotracking().

Returns a new query where the entities returned will not be cached in the DbContext or ObjectContext.

This appears to be the case for all objects in the object graph.

You just have to really understand your graph and what you do and don't want inserted/duplicated into the DB.

Lets assume we have objects like:

public class Person
{
  public int ID { get; set; }
  public string Name { get; set; }
  public virtual ICollection<Address> Addresses { get; set; }
}

public class Address
{
  public int ID { get; set; }
  public AddressLine { get; set; }
  public int StateID { get; set; }

  public ICollection<State> { get; set; }
}

So in order to Duplicate a person, I need to duplicate the addresses, but I don't want to duplicate the States.

var person = this._context.Persons
  .Include(i => i.Addresses)
  .AsNoTracking()
  .First();

// if this is a Guid, just do Guid.NewGuid();
// setting IDs to zero(0) assume the database is using an Identity Column
person.ID = 0;

foreach (var address in person.Addresses)
{
  address.ID = 0;
}

this._context.Persons.Add(person);
this._context.SaveChanges();

If you then wanted to then reuse those same objects again to insert a third duplicate, you'd either run the query again (with AsNoTracking()) or detach the objects (example):

dbContext.Entry(person).State = EntityState.Detached;
person.ID = 0;
foreach (var address in person.Addresses)
{
  dbContext.Entry(address).State = EntityState.Detached;
  address.ID = 0;
}

this._context.Persons.Add(person);
this._context.SaveChanges();
like image 130
Erik Philips Avatar answered Nov 07 '22 18:11

Erik Philips


You need to make correct deep copy of the whole entity graph - the best way is to serialize the original entity graph to memory stream and deserialize it to a new instance. Your entity must be serializable. It is often used with DataContractSerializer but you can use binary serialization as well.

like image 9
Ladislav Mrnka Avatar answered Nov 07 '22 18:11

Ladislav Mrnka