4

請考慮以下數據庫表。不幸的是,桌子不能以任何方式改變。EntityFramework與組合鍵的核心關係

Database schema

Houses有一個名爲Id一個自動遞增ID字段,命名爲Name一個字符串字段,並命名爲AreaId整型字段。後者不是Areas表的外鍵。

Areas具有由AreaId,CountryIdLangId組成的組合鍵。與AreaId相同的區域可以存在,但可以使用不同的CountryIdLangId。例如:可以有兩排具有相同的AreaId但不同的LangId

注意:爲什麼House有多個Area? A House沒有多個Area's, it only has one區域. The區域表有一個組合鍵,這意味着特定的行將有多個轉換。例如:區域ID 5可能具有英語LangId 5和西班牙語LangId 3。

這兩個表由以下兩個C#類描述。

public class House 
{ 
    public int Id { get; set; } 

    [MaxLength(80)] 
    public string Name { get; set; } 

    public int? AreaId { get; set; } 

    [ForeignKey("AreaId")] 
    public List<Area> Areas { get; set; } 
} 

public class Area 
{ 
    public int AreaId { get; set; } 

    public int CountryId { get; set; } 

    public string LangId { get; set; } 

    public string Name { get; set; } 
} 

組合鍵在上下文中定義,完全如文檔中所述。

protected override void OnModelCreating(ModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Area>() 
     .HasKey(a => new { a.AreaId, a.CountryId, a.LangId }); 
} 

例如,讓我們獲取數據庫中所有房屋的列表,包括它們各自的區域。

_context.Houses.Include(h => h.Areas).ToList(); 

在輸出窗口中生成以下SQL,並且生成的List包含與區域錯誤匹配的房屋。

SELECT [a].[AreaId], [a].[CountryId], [a].[LangId], [a].[Name] 
FROM [Areas] AS [a] 
WHERE EXISTS (
    SELECT 1 
    FROM [Houses] AS [h] 
    WHERE [a].[AreaId] = [h].[Id]) 
ORDER BY [a].[Id] 

正如你所看到的,涉及的EntityFramework與[a].[AreaId][h].[Id][h].[AreaId]。我如何在EF中表達這種關係?

+0

可以顯示組合密鑰上的配置代碼嗎? – Sampath

+0

我編輯了這個問題,添加了複合鍵的配置 – giannoug

+0

我不認爲這是可能的。很奇怪的數據庫設計順便說一句,甚至不合邏輯 - 一個房子如何在幾個地區等等。無論如何。 –

回答

9

您將無法在EF中正確映射此值。如果您想要House來引用Area,外鍵應該包含與Area的組合鍵相同的字段,否則EF將不接受映射。解決方法可能是跳過映射並在必要時手動加入實體,但這隱藏了真正的問題:糟糕的設計

主要的設計缺陷是您必須在添加翻譯時複製Area。現在的問題是 - 並且總是會是 - 哪個記錄代表我的實體Area實體?關係數據庫的基本前提是實體由獨特的記錄表示。您的設計違反了該核心原則。

不幸的是,表格不能以任何方式改變。

那麼,他們應該是!這樣離開它甚至不應該被考慮。您不應該使用扭曲的關係模型,這對順利開發應用程序來說太關鍵了。

的模型,我可以從你的描述拼湊它,也許應該是這樣的:

public class House 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public int? AreaId { get; set; } 
    public Area Area { get; set; } 
} 

public class Area 
{ 
    public int Id { get; set; } 
    public int CountryId { get; set; } 
    public Country Country { get; set; } 
    public string Name { get; set; } // E.g. the name in a default language 
    public ICollection<AreaTranslation> AreaTranslations { get; set; } 
} 

public class AreaTranslation 
{ 
    public int AreaId { get; set; } 
    public int LanguageId { get; set; } 
    public string LocalizedName { get; set; } 
} 

public class Country 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Language 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

對於這個模型,你需要一個明確的映射指令(EF會推斷出剩下的):

modelBuilder.Entity<AreaTranslation>() 
      .HasKey(a => new { a.AreaId, a.LanguageId }); 

你會發現Area現在真正代表了一個物理區域。 A House現在自然有一個Area,而不是這個奇怪的收集Area s必須被視爲一個地區。 AreaTranslation交界點類別發揮各種語言的作用。我假設Area屬於一個Country

+1

感謝您的詳細解答!我已經繼承了數據庫,這不是我的設計(我不是數據庫架構師,但我可以使用一些常識)。你給了我一些堅實的觀點,以加強我對新模式的立場 – giannoug