实体框架不更新外键对象

5

我是一个Entity Framework的新手,以下这种行为让我感到困惑:

    [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; }

         ...
    }

插入新实体时一切正常——ContactData已保存到表中并分配了外键:

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

但是当我尝试更新这个实体时,ContactData没有得到更新。

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

我需要将ContactData标记为已修改吗?还是我做错了什么?

编辑-2 - 答案

如果contactData是数据库中的新对象,并具有新对象id,请使用此代码。

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

如果您只想更新旧的contactData,则使用以下代码是正确的:
SharedContext.Current.Entry(contactData).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

编辑 - 代码片段扩展

MVC 控制器中的代码,页面的回传。参数 "clinicProfile" 和 "contactData"、"addressData" 包含有效的 ID。

        [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 });
        }

好的,首先您应该从数据库中检索contactData,对更改的字段进行更改,然后检查clinicprofile contactdataId是否与contactData.Id相同=>如果相同则无需操作,否则将clinnicProfile.ContactDataId更改为新的contactData.Id。 - Monah
3个回答

2

您只是错误地放置了外键属性

 [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; }

         ...
    }

当您想设置外键数据时,您可以设置ContactDataId值,也可以从数据库中检索ContactData作为对象并将其设置在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 });
        }

// 作为评论讨论的结果

如果您想更新contactData,您需要告诉上下文contactData已被修改,通过将其状态设置为modified。正如您在最后一篇帖子中提到的那样,如果您执行以下操作,它将起作用:

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

希望这能帮助到您。


我确信这是一个问题,所以我改变了代码并重新生成了数据库,但行为仍然相同 :( - Oleg Bul
你能添加更多的代码展示你试图进行的整个更新过程吗? - Monah
如果我将contactData标记为State.Modified,它会按预期更新。但是,据说不用标记子对象也应该可以正常工作? - Oleg Bul
当您更新与profileClinic相关的contactData时,您不需要更新profileClinic.ContactData,因为关系不会改变,除非您想完全更改profileClinic.ContactData(我的意思是更改为另一个新ID,因此您想将profileClinic链接到另一个ContactData)。在这种情况下,您需要更新profileClinic。希望我解释得清楚。 - Monah
只要按照你所说的,将contactData标记为已修改状态,一切都应该完美运作,除非你想将contactData从profileClinic中分离并分配给另一个联系人数据,如果是这样,那么你需要更新profileClinic.ContactDataId为新的ContactData.Id。希望这能帮到你。 - Monah
啊哈,所以你说,使用下面的代码来更新clinicProfile数据和contactData数据是正确的。在这种情况下,请将其包含在您的回答中,我会标记它。"SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified; SharedContext.Current.Entry(contactData).State = EntityState.Modified; SharedContext.Current.SaveChanges();"我以为EF会级联更新子对象,感谢你澄清这不是真的。 - Oleg Bul

0

在更新时,您只需要设置ContactDataId即可。

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

是的,ContactData对象中的Id也是设置好了的。如果我更改这两个Id中的任何一个,它都会抛出冲突异常,但如果Id相同且有效,则仍不会更新ContactData属性。 - Oleg Bul
它向您抛出了什么异常?您能提供更多细节吗? - Monah
1
发生了引用完整性约束违规:定义引用约束的属性值在关系中的主体对象和从属对象之间不一致。如果ID有效,则不执行任何操作(即不更新子对象)。 - Oleg Bul

0
如果您要将新的ContactData设置为现有的ClinicProfile,您应该先将新的ContactData保存到数据库中,然后获取其新的Id,最后使用新的ContactDataId更新ClinicProfile。
clinicProfile.ContactDataId = newContactData.Id;
SharedContext.Current.Entry(clinicProfile).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

如果您想更新现有的ContactData属性,则应仅保存它。
SharedContext.Current.Entry(existingContactData).State = EntityState.Modified;
SharedContext.Current.SaveChanges();

如果要插入,下面的代码将起作用:"clinicProfile.ContactData = contactData; SharedContext.Current.Entry(clinicProfile).State = EntityState.Added; SharedContext.Current.SaveChanges();" - Oleg Bul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接