首先使用EF代码进行继承-每个具体类型(TPC)的表

时间:2019-01-02 11:47:56

标签: c# asp.net asp.net-mvc entity-framework ef-code-first

我创建了一种Table per Concrete Type (TPC)结构,如下所示:

enter image description here

这里是这里使用的实体:

public abstract class BaseModel : MyOtherBaseClass
{
    [Key]
    public int Id { get; set; }

    //Foreign key for Project
    public int ProjectId { get; set; }

    public int Sequence { get; set; }

    public string Name { get; set; }

    public string IconUrl { get; set; }

    //Navigation Properties ####################
    public virtual Project Project { get; set; }
}


[Table("Tool")]
public class Tool : BaseModel
{
    [Key]
    public int Id { get; set; }

    public string ToolBrand { get; set; }

    //Navigation Properties ####################
    //public virtual Project Project { get; set; }
}


[Table("Priority")]
public class Priority : BaseModel
{
    [Key]
    public int Id { get; set; }

    public string PriorityCode { get; set; }

    //Navigation Properties ####################
    //public virtual Project Project { get; set; }
}

在这一点上,我对以下问题感到困惑:

1)我可以对工具和优先级实体使用ID吗?绝对没有必要吗?

2)我在BaseModel类中使用FK(ProjectId)和相关的表Project。但是,由于将在“工具”和“优先级”表中创建ProjectId列,因此如何创建关系?可能有什么问题吗?

3)如果我不喜欢Fluent API,是否应该在上下文中添加BaseModel实体之外的Tool和Priority实体?因为在某些资源中添加了子类,而在另一些资源中则没有。哪个是真的?

public class EntityContext : DbContext
{
    public DbSet<BaseModel> BaseModel { get; set; }
    // ? public DbSet<Tool> Tool { get; set; }
    // ? public DbSet<Priority> Priority { get; set; }
}

如果对此用法有任何疑问,也可以让我知道吗?谢谢...

1 个答案:

答案 0 :(得分:0)

每当设计类时,您都应该知道类的真正含义。

您的DbSet<...>代表数据库的一个表。 DbSet中的类将该表的列表示为非虚拟属性,并将表之间的关系表示为虚拟属性。

在我看来,您想要数据库中有两个表:一个带有Tools的表和一个带有Priorities的表。您目前认为Tools的某些列也在Priorities中。因此,您打算创建一个通用的基类,其名称比BaseModel更好。

您不能为通用基类发明合适的名称这一事实应该警告您,也许这两者之间并没有真正的共同点。您确定,如果描述一个Tool,那不是偶然的事,因为它具有与Priority相同名称和类型的某些属性

例如。如果要定义Tools行和Priorities行,您会说Tool中使用了每个Project,而每个PriorityProject。这是系统设计的一部分。根据您的定义,Tools没有其Project

是没有意义的

即使如此,根据您的定义,每个工具都应该有一个“唯一标识方法”,您决定为此使用整数ID。同样,您决定为优先级设置一个整数ID。但是,Tool表中的一行是否固有,标识的类型等于优先级的标识的类型?如果有人告诉您Tool有一个Guid ID,而Priority有一个整数ID,您是否希望您的设计一文不值?

当然不能:您的设计应该足够健壮,以至于数据库表中的细微变化都会导致您的设计中的细微变化!

哪些属性应该在基类中

  

1)我可以对工具和优先级实体使用ID吗?

答案:是的,您可以将Id放在基类中,并从派生类中忽略它。但是,这将增加一个约束,即每个派生类型的表中代表ID的列的名称和类型都应相同。

因此:如果您不希望局限于此:不要这样做,请从基类中删除ID并将其放在派生类中。如果以后有人决定进行较小的更改以重命名该列,或者给它提供其他类型,则您的更改将很小。

当然,这也适用于所有其他属性:如果它们“通过巧合”相同,则将值放在派生类中。如果ToolsPriorities都具有相同的典型特征,请将其放在基类中。

要检测项目的工具与项目的优先级之间的相似性对我来说并不容易,因此我将切换到另一个示例。

假设您有一个包含“教师和学生”的数据库。有些属性对于教师是唯一的,而某些属性对于学生是唯一的。但他们也有一些共同点,并非巧合:老师和学生都是人,都有名字,生日,可能还有地址等。如果后来有人决定某个地址有一个额外的字段,指示该地址的GPS坐标,则只需更改一个班级。

  

结论:仅将基类中所有派生类所固有的属性放进去,而不是巧合

将外键放在哪里?

根据您的设计,“工具”和“优先级”均属于项目。如果在上一步之后您确定这是它们唯一的共同点,那么您将很少选择一堆同时包含“工具”和“优先级”的对象。

在学校数据库中,将学生和教师分成一堆人是很正常的,其中每个人都有一个地址,而每个地址中将居住零个或多个人(一对多)< / p>

// this will be a row in a table, hence it has an Id
class Address
{
     public int Id {get; set;}
     public string ZipCode {get; set;}
     public string Street {get; set;}
     ...

     // on every address live zero or more Persons (one-to-many)
     public virtual ICollection <Person> Persons {get; set;}
}

// this will not be a row in a separate table, hence it has no ID
class Person
{
     public string Name {get; set;}
     public DateTime Birthday {get; set;}
     ...
     // every Person lives at an Address, using foreign key
     public int AddressId {get; set;}
     public virtual Address Address {get; set;}
}

class Teacher : Person
{
    public int Id {get; set;}
    ...
}
class Student: Person
{
    public int Id {get; set;}
    ...
}

因此,您将拥有三个表:地址,教师,学生。老师和学生都将具有一个人的属性。他们俩都住在同一个地址。

看看如果我们决定在“教师”或“人”中添加一列,几乎不需要进行任何更改?如果您希望教师ID为GUID或Address.Id为字符串(需要更改地址的主键和Person内部的外键),则几乎不需要更改。看看您想添加新的Person类型:Parent,需要多少更改?

  

结论:如果您有一个基类,则每个派生类都应在另一个表中引用一个项目:将外键放在基类中。但是,如果这种关系不是所有派生项所固有的,则将外键放在派生类中。

3我应该在上下文中添加除BaseModel实体之外的实体吗?

请记住:DbContext中的每个DbSet都将成为一个表。如果您未将“工具和优先级”指定为单独的表,则不会使用Table per concreate class (TPC),而是使用table per hierarchy (TPH):“工具和优先级”都将在一个表中

由于在表中得到的所有未使用的空值,我很少使用TPH。

如果您最经常询问“ ...的老师”和“ ...的学生”,则应将其保存在单独的表格中。基类的列也位于这些单独的表中。

如果您最经常询问“ ...的人”,则“人”可能是学生或教师,请考虑使用table per type (TPT):“人”表和具有“人”外键的“教师”表表以及带有外键的学生表。所有基类属性都在“人员”表中。

很容易看到,如果您要求“ Persons that ...”,TPT将仅查询一个表,而对于TPC,则需要查询Teachers表和Students表并将结果连接起来。

但是,如果您要求“ Students that”,则TPT将需要加入Persons表和Students表。 TPC的速度更快:只访问一个表。