Unable to update Foreign Key in Entity Framework 6

21,094

Solution 1

Since you are working with independent association. You can either

  • Adding and removing the relationship from ContactList, but you need to retrieve from both Person.

    db.Entry(newContact).State = EntityState.Modified;
    
    var p1 = db.Set<Person>().Include(p => p.ContactList)
        .FirstOrDefault(p =>p.Id == 1);
    p1.ContactList.Remove(newContact);
    
    var p3 = db.Set<Person>().Include(p => p.ContactList)
        .FirstOrDefault(p => p.Id == 3);
    p3.ContactList.Add(newContact);
    
    db.SaveChanges();
    
  • Or you can use disconnected object, but you need to manually manage the relationship.

    db.Entry(newContact).State = EntityState.Modified;
    
    var p1 = new Person { Id = 1 };
    db.Entry(p1).State = EntityState.Unchanged;
    var p3 = new Person { Id = 3 };
    db.Entry(p3).State = EntityState.Unchanged;
    
    var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
    manager.ChangeRelationshipState(newContact, p1, item => item.ContactOwner,
         EntityState.Deleted);
    manager.ChangeRelationshipState(newContact, p3, item => item.ContactOwner,
         EntityState.Added);
    
    db.SaveChanges();
    

PS

You might need to reconsider adding foreign key value, to make everything easier, updating foreign key just by mentioning the Id.

See this post for more information.

Solution 2

This is what I ended up building till the late hours of the morning, chould/should be refactored some more...

   protected static async Task<int> SaveEntity<t>(t obj) where t : BaseModel
    {
        try
        {
            using (DatabaseContext db = GetDbContext())
            {

                //get the basemodel/fk reference properties
                IEnumerable<PropertyInfo> props = obj.GetType().GetProperties().Where(p => p.PropertyType.BaseType == typeof(BaseModel));

                if (obj.Id <= 0)
                {//insert

                    db.Entry(obj).State = EntityState.Added;

                    //set fk reference props to unchanged state
                    foreach (PropertyInfo prop in props)
                    {
                        Object val = prop.GetValue(obj);
                        if (val != null)
                        {
                            db.Entry(val).State = EntityState.Unchanged;
                        }
                    }

                    //do insert
                    return await db.SaveChangesAsync();

                }
                else
                {//update

                    //get the posted fk values, and set them to null on the obj (to avaid dbContext conflicts)
                    Dictionary<string, int?> updateFKValues = new Dictionary<string, int?>();
                    foreach (PropertyInfo prop in props)
                    {
                        BaseModel val = (BaseModel)prop.GetValue(obj);
                        if (val == null)
                        {
                            updateFKValues.Add(prop.Name, null);
                        }
                        else
                        {
                            updateFKValues.Add(prop.Name, val.Id);
                        }

                        prop.SetValue(obj, null);
                    }

                    //dbContext creation may need to move to here as per below working example
                    t dbObj = (t)db.Set(typeof(t)).Find(new object[] { obj.Id }); //this also differs from example

                    //update the simple values
                    db.Entry(dbObj).CurrentValues.SetValues(obj);

                    //update complex values
                    foreach (PropertyInfo prop in props)
                    {
                        Object propValue = null;
                        if (updateFKValues[prop.Name].HasValue)
                        {
                            propValue = (BaseModel)db.Set(prop.PropertyType).Find(new object[] { updateFKValues[prop.Name] });
                        }

                        prop.SetValue(dbObj, propValue);
                        if (propValue != null)
                        {
                            db.Entry(propValue).State = EntityState.Unchanged;
                        }

                    }

                    //do update
                    return await db.SaveChangesAsync();

                }

            }
        }
        catch (Exception ex)
        {
            ExceptionHelper.Log(ex);
            throw;
        }
    }

Solution 3

Basically this happens because EntryState.Modified just looks for scalar properties (primitive types) and with independent association (your case) you don't have it.

There is a several ways to achieve this, @Yuliam has pointed some of them and here you can find more.

Share:
21,094
foo0110
Author by

foo0110

I'm a C# Web Application Programmer and know some simple Microsoft SQL Server Management.

Updated on July 15, 2020

Comments

  • foo0110
    foo0110 almost 4 years

    I am trying to do a simple update to the foreign key but the script never get sent over.

    Here is the code I am using:

    using (var db = new MyContext())
    {
          db.Entry<Contact>(newContact).State = EntityState.Modified;
          newContact.ContactOwner = db.Person.Find(3);
          db.SaveChanges();
    }
    

    EF6 update the rest of the column in the Persons table but it is not updating the Contact_Id in Persons table.

    Person entity:

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Contact> ContactList { get; set; }
    }
    

    Contact entity:

    public class Contact
    {
        public int Id { get; set; }
        public string Email { get; set; }
        public string TelNo { get; set; }
        public Person ContactOwner { get; set; }
    }
    

    What am I missing here?

    Please help!