Paul
Paul

Reputation: 467

How to update one to many relationship

I have one entity suppose [MainEntity]. This main entity has some child entity like below:

MainEntity:
          -> ChildEntity1
          -> ChildEntity2
          -> ChildEntity3
          -> ChildEntity4
          -> ChildEntity5

All child entity has one to many relationship with main entity.

In UI user update data for main entity as well as first 3 child entity. In UI ChildEntity1, ChildEntity2, ChildEntity3 are represented by GridView,ListBox and CheckListBox. User can add/update child record thru these control. Sample code to insert/update data as given below:

BL class:
========
public class EntityManager
 {
      public EntityManager ()
      {
            MainEntityData = new MainEntity();
      }
      public MainEntity MainEntityData= { get; set; }

      public void InsertUpdate()
      {
            DAL.InsertMainEntityData(MainEntityData);

      }
}


UI Codes:
=========

EntityManager objEntityManager = new EntityManager();

if (lnkInsertUpdate.CommandArgument == "Update")
   objEntityManager.MainEntity.ID =                        
                         int.Parse(Request.QueryString["ID"].ToString());

objEntityManager.MainEntity.Name = txtName.Text;
objEntityManager.MainEntity.Description = txtDescription.Text;
-----------------------------------------
-----------------------------------

ChildEntity1 objChildEntity1 = null;
foreach (ListItem item in lstSelectedProduct.Items)
{
  objChildEntity1 = new ChildEntity();
  objChildEntity1.ClientProductID = int.Parse(item.Value);
  objChildEntity1.IsActive = true;
  objChildEntity1.CreatedBy = "Admin";
  objChildEntity1.CreatedDate = DateTime.Now;
  objEntityManager.MainEntity.ChildEntitys1.Add(objChildEntity1);
}

-----------------------------------
codes for childentity2,childentity3
--------------------------------
objEntityManager.InsertUpdate()


DALCodes:
=========
public static void InsertMainEntityData(MainEntity objMainEntity)
{
    using (TransactionScope ts = new TransactionScope())
    {
      try
      {
        using (DBEntities context = DatabaseFactory.GetContext())
        {
        if (objMainEntity.ID > 0)
        {
         MainEntity mainEntityToUpdate = 
 context.MainEntitys.Include("ChildEntity1").Include("ChildEntity2").Include("ChildEntity3").First(o => o.ID == objMainEntity.ID);

          mainEntityToUpdate = objMainEntity;
          context.MainEntitys.Attach(mainEntityToUpdate);
         context.ObjectStateManager.ChangeObjectState(mainEntityToUpdate, 
                       EntityState.Modified);
        }
       else
        {
                context.AddToMainEntitys(objMainEntity);
                context.SaveChanges();
                 ts.Complete();
           }
          catch (Exception ex)
          {
            ts.Dispose();
          }
           finally
           {
        }
}

Insert new reord is working fine but while updating its giving following error:

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

Please guide how to resolve this issue??

Thanks,

Paul

Upvotes: 0

Views: 348

Answers (1)

Slauma
Slauma

Reputation: 177163

You have this error because you are overwriting the loaded mainEntityToUpdate with the detached objMainEntity in this line:

mainEntityToUpdate = objMainEntity;

Then you attach this object:

context.MainEntitys.Attach(mainEntityToUpdate);

Because mainEntityToUpdate is still attached to the context (after loading it) you have two objects with the same key attached to the context which is forbidden and causes the exception.

You can update the scalar properties of an entity by using instead:

MainEntity mainEntityToUpdate = context.MainEntitys
    .First(o => o.ID == objMainEntity.ID);

context.MainEntitys.ApplyCurrentValues(objMainEntity);

But it only updates scalar properties, not your child collections - as setting the state to Modified also doesn't cause an update for the child collections.

Updating the main entity including all child collections is actually not that easy. You must load the original entity including all child collections from the database (your start to load the main entity with Include("ChildX") is correct) and then compare it with the detached objMainEntity and its updated collections. "Compare" means: you must detect which collection item has been added by the user, which has been removed and which is still there (but perhaps with changed property values) and then add or remove items to/from the original's collections accordingly. Change detection will follow those changes and write the appropriate SQL statements to the database when you call SaveChanges.

A sketch how to do this (for DbContext/EF >= 4.1) is here: https://stackoverflow.com/a/5540956/270591

Upvotes: 1

Related Questions