【问题标题】:Entity Framework - Botched association实体框架 - 拙劣的关联
【发布时间】:2016-01-27 00:07:52
【问题描述】:

我正在尝试使用 Entity Framework 6.1.3 创建“数据库中的代码优先”类,但似乎 E.F. 无法建立关联(或者我只是没有在我的数据库中正确设置某些内容)。

这是有问题的两个表,我正在使用实体框架制作类;

在我的数据库中,CVE 与 AssessmentChecks 是一对多的关系。因此,AssessmentChecks 表中有一个外键;

ALTER TABLE NVD.[AssessmentChecks] ADD CONSTRAINT
    FK_AssessmentChecks_CVE FOREIGN KEY
    (
    CveIDFK
    ) REFERENCES NVD.CVE
    (
    CveID
    ) ON UPDATE  NO ACTION 
     ON DELETE  NO ACTION 

但是,当我从该数据库创建类时,Entity Framework 将“CveIDFK”标记为必填字段...

public partial class AssessmentCheck
{
    public int AssessmentCheckID { get; set; }

    [Required]
    [StringLength(20)]
    public string CveIDFK { get; set; }

    public string AssessmentCheckSystem { get; set; }

    public string AssessmentCheckURL { get; set; }

    [StringLength(200)]
    public string AssessmentCheckName { get; set; }

    public virtual CVE CVE { get; set; }
}

如果我使用同一个数据库来创建一个 EDMX 文件(EF 设计器,而不是“代码优先”),那么模型图和 AssessmentCheck 类的外观如下(这与我的预期相符);

public partial class AssessmentCheck
{
    public int AssessmentCheckID { get; set; }
    public string CveID { get; set; }
    public string AssessmentCheckSystem { get; set; }
    public string AssessmentCheckURL { get; set; }
    public string AssessmentCheckName { get; set; }

    public virtual CVE CVE { get; set; }
}

什么给了?如何让实体框架在代码优先模型中创建关联,使“CveIDFK”不是“必需”?

明确地说,我的最终目标是从数据库创建类,创建“CVE”对象,将“AssessmentCheck”添加到“CVE”对象,然后添加“SaveChanges”。问题是当我这样做并尝试保存更改时,EF 说我需要明确提供“AssessmentCheck”对象的“CveIDFK”字段。鉴于 CVE 和 AssessmentChecks 之间的关系,我认为应该自动暗示这个 FK。我错过了什么吗?


编辑:

我更新了数据库以返回使用 CveID" 作为 AssessmentChecks 表中的外键名称,并重新创建了我的类。

这是我用来保存数据的代码;

CVE cve = new CVE
            {
                CveID = "CVE-2016-2140"
            };

            AssessmentCheck AC1 = new AssessmentCheck
            {
                AssessmentCheckName = "First Assessment check"
            };

            AssessmentCheck AC2 = new AssessmentCheck
            {
                AssessmentCheckName = "Second Assessment Check"
            };

            cve.AssessmentChecks.Add(AC1);
            cve.AssessmentChecks.Add(AC2);

            using (var context = new NVDModel())
            {
                try
                {
                    context.Database.Log = Console.WriteLine;
                    context.CVEs.Add(cve);       
                    context.SaveChanges();
                }
            }

我得到的错误是

处于“已添加”状态的“AssessmentCheck”类型实体具有以下内容 验证错误: - 属性:“CveID”,错误:“CveID 字段是必需的。”处于“已添加”状态的“AssessmentCheck”类型实体具有以下验证 e 错误: - 属性:“CveID”,错误:“CveID 字段是必需的。”

如果我从“AssessmentChecks”类的“CveID”字段中删除“必需”数据注释,它可以工作(外键自动插入而我没有明确提供),但我认为我不必删除它数据注释。

另外,如果我在将父级添加到上下文之后移动子级的添加,那也可以;

try
                {
                    context.Database.Log = Console.WriteLine;
                    context.CVEs.Add(cve);
                    cve.AssessmentChecks.Add(AC1);
                    cve.AssessmentChecks.Add(AC2);
                    context.SaveChanges();
                }

这是怎么回事?

【问题讨论】:

  • 完全跑题了,但我把这个标题读作“拙劣的暗杀”。
  • 在您的数据库截图中,CvelDFK 被标记为NOT NULL(因此是必需的)。在这种情况下,EF 做了正确的事情
  • @Rob 它绝对是必需的,但是在代码中,当我创建一个 CVE 对象并向它添加一个 AssessmentCheck 时,然后“SaveChanges”我认为 EntityFramework 应该自动填充“CveIDFK”外键字段,但它没有。相反,它告诉我 CveIDFK 字段是必需的,因此“保存”失败。我相信我不应该明确设置“CveIDFK”,它应该自动从关联中派生
  • 我没有看到您首先在代码中进行关联的位置。如果将 CveIDFK 更改为 CveId(请注意这是数据库首先生成的内容),则可以按照惯例进行操作。如果在 CVE 对象上方添加 ForeignKey("CveIDFK"),则可以通过注释来完成。或者您可以使用 fluent api 来完成。 msdn.microsoft.com/en-us/data/jj819164.aspx显示保存时实际抛出错误的代码。
  • @SteveGreene 我很困惑,当我首先从数据库编写代码时,EF 不应该为我建立关联吗?它不应该承认这种关系吗?

标签: c# sql-server entity-framework ef-code-first edmx


【解决方案1】:

一旦您将实体添加到 dbset(在您的情况下为 CVE)实体框架开始跟踪对其所做的任何更改,因此当您将子对象(AssessmentCheck)添加到该实体中时,它能够通过提供的信息分配父 ID由您,即在代码优先的情况下进行数据注释。因此,第二种方法是有效的。

在第一种方法中,如果您使用身份(即自动生成)而不是为 CVE 提供 PK(CveId),那么您将能够实现相同的效果,但由于您无法在 CVE 身份中使用字符串作为 PK,因此简单的解决方案是将 int 作为 PK 并使现有的 CveId 成为唯一键,否则使用自定义添加而不是 cve.AssessmentChecks.Add... 将 FK 分配/设置给子项。

如果你在保存更改之前使用以下方法可以看到这个动作

var entitychanges = context.ChangeTracker.Entries().Where(e => e.State == EntityState.Added);

您会注意到,在第一种方法中 CveID 未设置为 AssessmentCheck,而在第二种方法中是。

【讨论】:

  • 感谢您的回复,尽管我仍然感到困惑。是否有 EF 的文档解释了为什么只能为身份/整数主键而不是字符串自动设置关系?另外,为什么在使用 EF EDMX 设计器而不是代码优先方法时第一种方法有效(在上下文之外添加评估检查)?唯一的区别是我使用“数据库中的 EF 设计器”生成类,而不是“数据库中的代码优先”,但对象创建/保存是相同的,并且它适用于 EF 设计器方法
  • FWIW,我只是最终使用了 Visual EDMX 模型,它没有受到“必须在上下文之外分配孩子的父母”问题的影响
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多