代码优先,EF 4.1:将下列类映射到数据库的最佳方法是什么?

本文关键字:数据库 映射 最佳 是什么 方法 EF 代码 | 更新日期: 2023-09-27 17:50:19

亲爱的专业开发者社区!

我想弄清楚如何使用代码优先正确地制作映射。

假设有几个类。一个容器类,一个基类和几个后代类。

基类:

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

子类:

public class FirstDescendant {
 public string FirstProperty { get; set; }
}
public class SecondDescendant {
 public int SecondProperty { get; set; }
}
public class ThirdDescendant {
 public int ThirdProperty { get; set; }
}

容器类:

public class Container {
 public int Id { get; set; }
 public ICollection<FirstDescendant> FirstDescendants { get; set; }
 public ICollection<SecondDescendant> SecondDescendants { get; set; }
 public ICollection<ThirdDescendant> ThirdDescendants { get; set; }
}

默认情况下,在创建DbContext并运行我的代码之后,我得到两个具有以下结构的表:

容器

IdBase table

Id

FirstProperty

Container_Id1

SecondProperty

Container_Id2

ThirdProperty

Container_Id3

所以我得到一个名为Base的表,它包含相同的三个字段,它们具有相同的外键作用。

现在的问题是:

  1. 我不喜欢那种冗余。专业人士如何评价这种情况,这正常吗?

  2. 我可以改变这个吗?我能得到一个更清晰的数据库结构吗?

  3. 映射这些类的最佳方法是什么?

我已经发布了一个类似的问题,但我仍然关心这个问题。

感谢您的回答和关注。

代码优先,EF 4.1:将下列类映射到数据库的最佳方法是什么?

默认情况下,数据库中的表结构是正常的,并且是每层次表(TPH)映射的结果。将类层次结构映射到关系数据库表是默认策略。在此策略中,所有派生类的属性被合并到一个表中,并且类型仅通过表中的标识符列来区分。

如果你想在层次结构中每个类都有一个单独的表,你可以使用 table - per - type (TPT)策略。但这不是默认值,你必须通过数据注释明确指定。

[Table("FirstDescendants")]
public class FirstDescendant : Base
{
    public string FirstProperty { get; set; }
}
// the same with the other derived classes

…或者在Fluent代码中:

modelBuilder.Entity<FirstDescendant>().ToTable("FirstDescendants");
// the same with the other derived classes

这样,您将获得Base类的表,该表仅包含该类的属性(在您的示例中仅包含Id)和单独的表FirstDescendants等,这些表具有派生类的属性。这两个表由一个外键关系链接,EF设法从两个表中加载所需的列,以实现您正在查询的类型的对象。

您可以在这里找到EF如何映射类层次结构的几种策略的介绍:

EF代码优先继承

编辑

在我看来,TPT是将派生类映射到关系表的更简洁的方法。TPH要求您在数据库中具有可空列,仅用于您可能希望在模型中具有所需属性的映射。但另一方面,TPH可能性能更高,因为查询不需要连接不同的表。

一个可能的经验法则是:

  • 如果大多数属性都在基类中,而派生类只添加了一些额外的属性,则可以使用TPH

  • 如果基类不包含属性或只有少数属性,并且大多数属性都在派生类中,则使用TPT。

但是这两种方法的优缺点——也是第三种每具体类表(TPC)——要好得多,在上面的链接系列文章中有详细的解释。