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