【问题标题】:Entity Framework Foreign Key attribute usage problems实体框架外键属性使用问题
【发布时间】:2014-10-07 16:16:38
【问题描述】:

我正在使用 Entity Framework 6 Code First 编写一个 ASP.NET MVC 5 应用程序,但外键有点问题。为了举例说明这个问题,我展示了两个表格,国家和货币。

Country 模型表示一个包含国家及其属性的表格:

[Table("dbo.Country")]
public class Country {
    [Key]
    [StringLength(2)]
    [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))]
    public string iso { get; set; }

    [Required]
    [StringLength(80)]
    [Display(Name = "L_Country", ResourceType = typeof(ResxGlobal))]
    public string name { get; set; }

    [Required]
    [StringLength(80)]
    public string nicename { get; set; }

    [StringLength(3)]
    [Display(Name = "L_Country_ISO3", ResourceType = typeof(ResxGlobal))]
    public string iso3 { get; set; }

    public short? numcode { get; set; }

    [Display(Name = "L_DialCode", ResourceType = typeof(ResxGlobal))]
    public int phonecode { get; set; }
}

我有一个名为 Currency 的单独表,其中列出了国家/地区的货币属性,其 PK 是两个字母的 ISO 国家代码,同时也是 Country 表的 FK:

[Table("dbo.Currency")]
public class Currency {
    [Key]
    [StringLength(2)]
    [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))]
    public string CountryCode { get; set; }

    [Required, StringLength(100)]
    [Display(Name="Currency name")]
    public string CurrencyName { get; set; }

    [Required, StringLength(3)]
    [Display(Name="Currency code")]
    public string CurrencyCode { get; set; }

    [StringLength(5)]
    public string Symbol { get; set; }

    //[ForeignKey("CountryCode")]
    public virtual Country Country {get; set; }
}

到目前为止一切都很好,所以现在看看第二个模型(货币)。如果我启用 ForeignKey 属性,指定 CountryCode PK 也是导航属性 Country 使用的 FK 并执行迁移,则 SQL Server 图表显示 Country 和 Currencya 之间的关系,PK(它们是小键)终止符关系的两端,而通常小键图标显示在 PK 表上,而小无穷大图标显示在关系的 FK 端。我觉得很奇怪。

我恢复了迁移,删除(注释掉)外键属性并再次更新了数据库。这次关系显示为我预期的 PK 键和无穷大图标的另一端。但是,该表显示了一个我没有在模型中指定的额外列。我手动编辑了迁移以省略我没有在模型中指定的名为“Country_iso”的额外列。

在 Update-Database 之前删除了(从迁移代码中)不需要的 Country_iso 列,我发现当我尝试使用数据库上下文检索货币时,我收到“Invalid column name Country_iso”错误。它到底在哪里坚持要获得我没有的专栏?我在任何地方都没有看到对它的引用,它是否隐藏在某个元数据文件的某个地方?

那么在模型中建立 FK 关系(一对一和一对多)的正确或适当方式是什么?

【问题讨论】:

    标签: ef-code-first entity-framework-6 entity-framework-migrations


    【解决方案1】:

    几周前,我发现有关 ForeignKey 属性使用的相互矛盾的信息,所以我坚持使用看起来最合乎逻辑的那个。

    但是,我做了另一个实验,然后将 ForeignKey 属性移动到 Currency 表/模型的 CountryCode 属性,如下所示:

     [Key]
     [ForeignKey("Country")]
     [StringLength(2)]
     [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))]
     public string CountryCode { get; set; }
            :
     public virtual Country Country {get; set; }
    

    所以它现在位于 PK 字段(也是一个 FK)并指向相应的导航属性(国家/地区)。

    因此,迁移“脚本”不会引入任何虚拟列(以前的 Country_iso 列),并且信息检索也不会产生任何错误。

    【讨论】:

      【解决方案2】:

      在主键上设置外键约束似乎不是一个好主意。

      考虑删除 Country 记录的场景,数据库无法在 Currency 表中强制执行 FK 约束,因为它不能将 FK 字段设为 NULL(它也是主钥匙)。您的货币记录将继续引用不存在的内容。

      当您没有提供外键并且无法从您的属性名称中解析时,EF 为您生成了 Country_iso 列。

      您的 Currency 表中有一个 CurrencyCode 属性,您可以将其用作主键。

      [Table("dbo.Currency")] 
      public class Currency {
      
          [StringLength(2)]
          [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))]
          public string CountryCode { get; set; }
      
          [Required, StringLength(100)]
          [Display(Name="Currency name")]
          public string CurrencyName { get; set; }
      
          [Key]
          [Required, StringLength(3)]
          [Display(Name="Currency code")]
          public string CurrencyCode { get; set; }
      
          [StringLength(5)]
          public string Symbol { get; set; }
      
          [ForeignKey("CountryCode")]
          public virtual Country Country {get; set; } 
      }
      

      这适用于每个只有一种货币的国家/地区,反之亦然 (1:1)。如果您想支持使用相同货币 (M:1) 的多个国家/地区,您希望每个国家/地区都引用一个货币记录。

      [Table("dbo.Country")]
      public class Country{
          [Key]
          [StringLength(2)]
          [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))]
          public string iso { get; set; }
      
          [Required]
          [StringLength(80)]
          [Display(Name = "L_Country", ResourceType = typeof(ResxGlobal))]
          public string name { get; set; }
      
          [Required]
          [StringLength(80)]
          public string nicename { get; set; }
      
          [StringLength(3)]
          [Display(Name = "L_Country_ISO3", ResourceType = typeof(ResxGlobal))]
          public string iso3 { get; set; }
      
          public short? numcode { get; set; }
      
          [Display(Name = "L_DialCode", ResourceType = typeof(ResxGlobal))]
          public int phonecode { get; set; }
      
          public string CurrencyCode { get; set; }
      
          [ForeignKey("CurrencyCode")]
          public virtual Currency Currency { get; set; }
      }
      
      [Table("dbo.Currency")]
      public class Currency{
          [Key]
          [Required, StringLength(3)]
          [Display(Name = "Currency code")]
          public string CurrencyCode { get; set; }
      
          [Required, StringLength(100)]
          [Display(Name = "Currency name")]
          public string CurrencyName { get; set; }
      
          [StringLength(5)]
          public string Symbol { get; set; }
      
          public virtual ICollection<Country> Countries { get; set; }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-10-12
        • 2010-10-03
        • 1970-01-01
        • 2011-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多