EntityFramework與復合鍵的核心關係

asp.net asp.net-core c# entity-framework entity-framework-core

請考慮以下數據庫表。不幸的是,表格不能以任何方式改變。

數據庫架構

Houses有一個名為Id的自動增量ID字段,一個名為Name的字符串字段和一個名為AreaId的整數字段。後者不是Areas表的外鍵。

Areas擁有一個由複合鍵AreaIdCountryIdLangId 。與同一個區域AreaId可以存在,但不同的CountryIdLangId 。例如:可以有兩行具有相同的AreaId但不同的LangId

注意:為什麼House有多個Area ?一個House沒有多個Area's, it only has one區域. The Area`s表有一個複合鍵,這意味著特定的行會有多個譯本。例如:區域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,結果列表包含與區域不正確匹配的Houses。

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中表達這種關係?

一般承認的答案

您將無法在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現在真正代表了那裡的物理區域。一個House現在自然有一個Area ,而不是這個奇怪的Area集合,必須以某種方式被視為一個區域。 AreaTranslation聯結類可以使用各種語言。我假設一個Area屬於一個Country



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow