2

I have a class Customer. I am trying to clone a Customer object and modify it, then I want those modifications to be reflected in the context (database as well). I am using following code to do that.

    Customer old = context.Customers.Where(c=>c.CustomerID ==1 ).SingleOrDefault();
    Customer m = CustomExtensions.ShallowCopyEntity<Customer>(old);
    m.Name = "Modified";
    m.MobileNo = "9999999999";

    context.Customers.Attach(m);

But its throwing following exception

Attaching an entity of type 'DataBindingSample.Customer' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

I tried changing EntityState to Modified but it didn't work.

Can anyone tell me how to achieve this?

My main goals are

  1. I want to clone (I will use deep clone when necessary) an existing entity
  2. Want to modify the cloned entity (as well as referenced entities - I will use deep clone in this case)
  3. Finally I want to save changes to database

EDIT

As pointed out in this comment i am trying to attach object which aready exists in context. So i can detach it first and then atttach again as shown bellow if attach is compulsory.

        Customer old = context.Customers.Where(c=>c.CustomerID ==1 ).SingleOrDefault();
        Customer m = CustomExtensions.ShallowCopyEntity<Customer>(old);
        m.Name = "Modified789789";
        m.MobileNo = "9999999999";
        ((IObjectContextAdapter)context).ObjectContext.Detach(old);
        context.Customers.Attach(m);
        context.Entry(m).State = EntityState.Modified;
        context.SaveChanges();

Otherwise i can follow 2 options mentioned in this answer.

15
  • did you try to give a new primary-key value to your clone before attempting to save it? Commented Jul 25, 2015 at 15:32
  • @jstreet, No, actually its not a new object, i want to modify an existing object. If i give new primary key its doesn't make sense. Commented Jul 25, 2015 at 15:33
  • problem is, when you clone an existing object, it is a new object and needs its own primary key. If you just want to modify an existing object then you should not clone it. You can't have it both ways. Commented Jul 25, 2015 at 15:36
  • I don't get why you're cloning it. Cloning by definition creates something new. It doesn't modify it. Commented Jul 25, 2015 at 15:53
  • @dman2306 the reason i am cloning it is, I am displaying Customer in a form where user will do all the modifications, and here all the modifactions are getting reflected in context (databinding). BUt if user choses to cancel all modifications its not possible. So i am cloning it so that all the modifcations will be done on cloned object and finally cloned object will be saved if user choses to save. Commented Jul 25, 2015 at 15:57

1 Answer 1

2

There are 2 options that I can think of:

  1. Copy the updated values back to the original entity loaded into your DbContext and then save changes.
  2. Updated values of the original entity and then discard them if user canceled the update.

Options 1

Just copy the updated values back to the originally loaded entity. Automapper is your friend in tasks like this. This approach can later be extended to allow user to change a model of your entity and not the data layer object itself (e.g. to expose a limited number of fields that user can edit).

var entity = context.Customers.SingleOrDefault(c => c.CustomerID == 1);
var updatedEntity = CustomExtensions.ShallowCopyEntity<Customer>(old);

updatedEntity.Name = "Modified";
updatedEntity.MobileNo = "9999999999";

entity.Name = updatedEntity.Name;
entity.MobileNo = updatedEntity.MobileNo;

context.SaveChanges();

If you add Automapper nuget, then you mappings (copying) will become much easier:

Mapper.CreateMap<Customer, Customer>();
Mapper.Map(updatedEntity, entity);

And your code will look like:

// Configuring mapping. Needs to be done only once.
Mapper.CreateMap<Customer, Customer>();

var entity = context.Customers.SingleOrDefault(c => c.CustomerID == 1);

// Check if entity is null

var updatedEntity = CustomExtensions.ShallowCopyEntity<Customer>(old);

updatedEntity.Name = "Modified";
updatedEntity.MobileNo = "9999999999";

// Copy the updated values back
Mapper.Map(updatedEntity, entity);

context.SaveChanges();

Options 2

Make changes in the originally loaded entity and discard them if user changed her mind and canceled. See this post and this post on how to do it. Discarding the whole DbContext might not be a good option in case you still need it (duh).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.