Rajeev
Rajeev

Reputation: 903

Unable to attach an existing object in Entity Framework 6

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.

Upvotes: 2

Views: 4297

Answers (1)

Nikolai Samteladze
Nikolai Samteladze

Reputation: 7797

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).

Upvotes: 2

Related Questions