5

I'm new to Entity Framework and this behavior confuses me:

    [Table("ClinicProfile")]
    public class ClinicProfile
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [ForeignKey("ContactData")]
        public int ContactDataId { get; set; }
        public ContactData ContactData { get; set; }
    }

    [Table("ContactData")]
    public class ContactData
    {
         [Key]
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
         public int Id { get; set; }

         ...
    }

When inserting the new entity all works fine - ContactData is saved to the table and foreign key assigned:

clinicProfile.ContactData = contactData;
SharedContext.Current.Entry(clinicProfile).State = EntityState.Added;
SharedContext.Current.SaveChanges();

But when I try to update this entity, ContactData don't get an update.

clinicProfile.ContactData = contactData;
SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

Must I mark ContactData as modified too? Or am I just doing something wrong?

EDIT-2 - The answer

Use this code, if contactData is the new object in the DB, with the new object id.

clinicProfile.ContactData = contactData;
SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

If you just want to update the old contactData, it would be correct to use this code:

SharedContext.Current.Entry(contactData).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

EDIT - extended code snapshot

Code from MVC controller, postback from the page. Parameters "clinicProfile" and "contactData", "adressData" contain valid Id's.

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Modify(ClinicProfile clinicProfile, ContactData contactData, AdressData adressData)
        {
            ViewBag.Id = clinicProfile.Id;

            if (ModelState.IsValid)
            {
                if (clinicProfile.Id != 0)
                {
                    clinicProfile.ContactData = contactData;
                    clinicProfile.AdressData = adressData;
                    clinicProfile.AdressDataComposed = adressData.ComposeData();
                    clinicProfile.ContactDataComposed = contactData.ComposeData();

                    SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
                    SharedContext.Current.SaveChanges();

                    Config.SaveClinicPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);
                    Config.SaveClinicPreviewPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);

                    return View(new ClinicProfileComposite { AdressData = adressData, ClinicProfile = clinicProfile, ContactData = contactData });
                }

                {
                    clinicProfile.ContactData = contactData;
                    clinicProfile.AdressData = adressData;
                    clinicProfile.AdressDataComposed = adressData.ComposeData();
                    clinicProfile.ContactDataComposed = contactData.ComposeData();

                    SharedContext.Current.Entry(clinicProfile).State = EntityState.Added;
                    SharedContext.Current.SaveChanges();

                    Config.SaveClinicPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);
                    Config.SaveClinicPreviewPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);

                    return RedirectToAction("Info", new { id = clinicProfile.Id });
                }
            }

            ViewBag.Id = clinicProfile.Id;
            return View(new ClinicProfileComposite { AdressData = adressData, ClinicProfile = clinicProfile, ContactData = contactData });
        }
Oleg Bul
  • 93
  • 1
  • 2
  • 8
  • ok, first you should retrieve the contactData from database, make the changes to the changed fields, then check if the clinicprofile contactdataId same as contactData.Id => nothing to do else change clinnicProfile.ContactDataId to the new contactData.Id – Monah Oct 26 '14 at 11:28

3 Answers3

2

you just misplaced the foreign key property

 [Table("ClinicProfile")]
    public class ClinicProfile
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        //[ForeignKey("ContactData")] here the wrong place
        public int ContactDataId { get; set; }
        [ForeignKey("ContactDataId")] // here the correct place
        public ContactData ContactData { get; set; }
    }

    [Table("ContactData")]
    public class ContactData
    {
         [Key]
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
         public int Id { get; set; }

         ...
    }

and when you want to set the foreign key data, you can either set the ContactDataId value or retrieve the ContactData from Database as object and set it in the ClinicProfile

// according to your data posted later

[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Modify(ClinicProfile clinicProfile, ContactData contactData, AdressData adressData)
        {
            ViewBag.Id = clinicProfile.Id;

            if (ModelState.IsValid)
            {
                if (clinicProfile.Id != 0)
                {
                    // here you want to tell the SharedContext to attach the contactData to the clinicProfile 
                    // you need to retrieve the lastVersion of contactData from db
                    var currentContactData=SharedContext.Current.ContactData.Single(t=>t.Id=contactData.Id);
                    // update the changed data in the currentContactData
                    clinicProfile.ContactData =currentContactData;  // instead of contactData;
                    clinicProfile.AdressData = adressData;
                    clinicProfile.AdressDataComposed = adressData.ComposeData();
                    clinicProfile.ContactDataComposed = contactData.ComposeData();

                    SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
                    SharedContext.Current.SaveChanges();

                    Config.SaveClinicPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);
                    Config.SaveClinicPreviewPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);

                    return View(new ClinicProfileComposite { AdressData = adressData, ClinicProfile = clinicProfile, ContactData = contactData });
                }

                {
                    clinicProfile.ContactData = contactData;
                    clinicProfile.AdressData = adressData;
                    clinicProfile.AdressDataComposed = adressData.ComposeData();
                    clinicProfile.ContactDataComposed = contactData.ComposeData();

                    SharedContext.Current.Entry(clinicProfile).State = EntityState.Added;
                    SharedContext.Current.SaveChanges();

                    Config.SaveClinicPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);
                    Config.SaveClinicPreviewPhoto(clinicProfile.ClinicImageUpload, clinicProfile.Id);

                    return RedirectToAction("Info", new { id = clinicProfile.Id });
                }
            }

            ViewBag.Id = clinicProfile.Id;
            return View(new ClinicProfileComposite { AdressData = adressData, ClinicProfile = clinicProfile, ContactData = contactData });
        }

// as result of comments discussion

if you want to update contactData , you need to tell the context that contactData was modified by setting its state to modified and as you mentioned in your last post, it will work if you make the following:

SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified; SharedContext.Current.Entry(contactData).State = EntityState.Modified; SharedContext.Current.SaveChanges();

hope that this will help you

Monah
  • 6,714
  • 6
  • 22
  • 52
  • I was sure this was an issue, so I changed the code and re-generated the database, but the behavior stays the same :( – Oleg Bul Oct 26 '14 at 11:13
  • can you add more code showing the whole update process you are trying to make? – Monah Oct 26 '14 at 11:17
  • If I mark contactData as State.Modified it updates as expected. But, supposedly, it should work correctly without marking child objects? – Oleg Bul Oct 26 '14 at 11:31
  • when you update contactData that is related to the profileClinic, you don't need to update the profileClinic.ContactData since the relation will not change unless you want to completely change the profileClinic.ContactData ( to another new Id i mean, so you want to link profileClinic to another ContactData) in this case you need to update the profileClinic, hope that i explained in good way – Monah Oct 26 '14 at 11:34
  • exactly what you said, contactData mark its state to modified and everything should work perfectly, unless you want to detach the contactData from profileClinic and to assign it to another contact data , if so then you need to update profileClinic.ContactDataId to the new ContactData.Id, hope this will help you – Monah Oct 26 '14 at 11:37
  • Aha. So, you say, that it would be correct to use the code below to update clinicProfile data and contactData data. In this case please include it into yuor answer and I'll mark it. "SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified; SharedContext.Current.Entry(contactData).State = EntityState.Modified; SharedContext.Current.SaveChanges();" I thought that EF cascadedly updates child objects, thanks for clarifing it is not true. – Oleg Bul Oct 26 '14 at 12:02
0

You just need to set ContactDataId, when updating

clinicProfile.ContactData = contactData;
clinicProfile.ContactDataId = contactData.Id;
SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
SharedContext.Current.SaveChanges();
Michael
  • 2,961
  • 2
  • 28
  • 54
  • Yes, it is set, as well as Id in ContactData object. When I change any of those two Id it throws conflict exception on me, but still don't update ContactData properties if Id's are the same and valid. – Oleg Bul Oct 26 '14 at 10:50
  • what exception it throws to you, can you put more details? – Monah Oct 26 '14 at 11:18
  • 1
    "A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship." If Id's are valid, it does nothing (in means of updating the child object) – Oleg Bul Oct 26 '14 at 11:34
0

if you set to existing ClinicProfile new ContactData you should first save new ContactData to db, second get its new Id and third update ClinicProfile with new ContactDataId.

clinicProfile.ContactDataId = newContactData.Id;
SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

if you want to update existing ContactData properties then you should save only it.

SharedContext.Current.Entry(existingContactData).State = EntityState.Modified;
SharedContext.Current.SaveChanges();
aleha_84
  • 8,309
  • 2
  • 38
  • 46
  • In case of insert the code below would work: "clinicProfile.ContactData = contactData; SharedContext.Current.Entry(clinicProfile).State = EntityState.Added; SharedContext.Current.SaveChanges();" – Oleg Bul Oct 26 '14 at 12:06