0

我正在用Entity Framework 6 Code First編寫ASP.NET MVC 5應用程序,並且遇到了一些外鍵問題。爲了舉例說明問題,我展示了兩個表格,國家和貨幣。實體框架外鍵屬性使用問題

的國家模型代表了國家和它們的屬性表:

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

而且我有一個單獨的表名爲貨幣,列出了其PK是兩個字母的ISO國家代碼這是該國的貨幣屬性在同一時間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; } 
} 

到目前爲止好所以現在看看第二個模型(貨幣)。如果我啓用指定CountryCode PK也是導航屬性Country使用的FK並執行遷移的ForeignKey屬性,則SQL Server圖表顯示國家和Currencya與PK(它們的小關鍵)終止符之間的關係關係的兩端,而通常情況下,小型鑰匙圖標顯示在PK表上,並且在關係的FK結束時顯示小無窮大圖標。我發現這很奇怪。

我恢復了遷移,刪除(註釋掉)了外鍵屬性並再次更新了數據庫。這一次的關係表現出我期望的PK與鑰匙和其他無窮大的圖標。但是,該表顯示了一個額外的列,我沒有在我的模型中指定。我手動編輯遷移以省略我的模型中未指定的名爲「Country_iso」的額外列。

在更新數據庫之前刪除(從遷移代碼)不需要的Country_iso列我發現當我嘗試使用數據庫上下文來檢索貨幣時,出現「Invalid column name Country_iso」錯誤。它在哪裏堅持要獲得我沒有的專欄?我沒有在任何地方看到這個引用,它隱藏在某個元數據文件的某處嗎?

那麼在模型中建立FK關係(一對一和一對多)的正確方法是什麼?

回答

1

幾周前,我發現有關使用ForeignKey屬性的衝突信息,所以我堅持看起來最符合邏輯的信息。

不過,我做了另一個實驗,然後移動ForeignKey的屬性爲貨幣表/模型的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列),並且信息的檢索也不會產生任何錯誤。

0

將外鍵約束放在主鍵上似乎不是一個好主意。

想想用戶刪除國家記錄的情況下,數據庫無法執行FK約束在貨幣表,因爲它不能爲null FK場(這也是主鍵)。您的貨幣記錄將繼續引用不存在的內容。

如果您沒有提供外鍵,且無法從您的屬性名稱解析EF,則爲您生成列Country_iso。

您的貨幣表中有一個可用作主鍵的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; } 
}