【问题标题】:C#/.NET inheritance best practiceC#/.NET 继承最佳实践
【发布时间】:2021-06-24 09:49:57
【问题描述】:

我想知道从基类具有 PK id 的基类继承的数据库架构的最佳做法是什么。

假设我正在为一所学校构建一个应用程序。有教师和工作人员,每个人都有自己的“职责”。我想要一个 Employee 基类。然后,我想要适用于任何类型员工的 ONE Duty 课程。意思是,我不想创建一个对 TeacherId 具有 FK 的 TeacherDuty 类和对 StaffId 具有 FK 的第二个 StaffDuty 类,我想要一个绑定到 EmployeeId 的职责类。

这里的最佳做法是什么?或者最好的做法是让每个班级都有一个带有 FK 的 TeacherDuty 和 StaffDuty 班级。我正在尝试尽量减少类的数量并使它们尽可能地可重用(例如,如果有另一种类型的 Employee,比如 Administrator,我想避免为 AdministratorDuty 创建另一个表)。

public class Employee
{
   public string Id {get; set;}
}

public class Teacher: Employee 
{
  public string Id {get; set;}
}

public class Teacher: Employee
{
  public string Id {get; set;}
}

public class Duty 
{
  //what's the FK here?
}

【问题讨论】:

  • 为什么需要TeacherIdStaffIdId 还不够吗?如果没有充分的理由,FK 可以是 Id
  • 首先。想想如何规范这个。教师是雇员。但在您的示例中,它也是员工。这是行不通的。此外,当教师继承 Employee 时,它​​也会继承其 Id。所以第二个 TeacherId 是无稽之谈。在深入研究代码之前制作模型的图像。
  • 是的,对不起,我打错了。
  • 您的新编辑没有多大意义。没有Staff,也没有TeacherIdStaffId。您是否可以编辑问题以使代码与问题匹配,反之亦然。
  • 如前所述。暂时不要深入研究代码。首先尝试思考你的模型。在纸上或其他任何东西上画一幅画,并定义您想要的实体及其依赖关系。在开始编码之前尝试并专注于问题。

标签: c# .net oop


【解决方案1】:

您的层次结构仍然不太合理,因为您在那里有两次相同的教师课程。我假设其中之一是Staff 而不是Teacher。但即便如此,我也不太清楚为什么老师也不会被视为员工?

在继续之前正确设计您的架构很重要。我将尝试回答您的实际问题,但这并不意味着这一定是解决问题的正确方法。最后我提出了一种替代架构设计供您考虑。

回答您的问题

为了回答你的问题,我们可以看看 ORM(即实体框架核心)是如何解决这种事情的。问题是,OOP 和类的概念并没有完全映射到关系数据库世界。 C# ORM 之类的 Entity Framework 必须这样做,所以即使您不打算使用 Entity Framework,看看他们如何解决这个问题可以让您深入了解。

为了更清楚地了解发生了什么,我将为您的每个类添加一些假列。让我们假设这是您的 C# 类的样子:

public class Employee
{
    public string Id { get; set; }
    public bool IsActive { get; set; } 
}

public class Administrator : Employee
{
    public string PhoneNumber { get; set; }
}

public class Teacher : Employee
{
    public string HomeRoom { get; set; }
}

因此,您可以看到管理员和教师应该共享一个 ID 以及他们是否处于活动状态。管理员存储他们的电话号码,教师需要一个家庭房间。管理员不需要家庭房间,教师不需要电话号码。同样,这些列并不意味着是一个真实的用例,只是作为一个例子。

每个层次结构的表

第一种方法(也是 Entity Framework Core 中推荐/默认的方法)是执行single table per Hierarchy。这意味着您将有一个名为Employee 的表,该表将包含表示每种类型的员工(员工、教师、管理员等)所需的所有列。这意味着将有 1 个EmployeeID 列代表所有员工。神奇之处在于有一个“鉴别器”列来指定“此员工是 X”。

所以你的Employee 表看起来像这样:

Column Type
Id NVARCHAR(50) PK
Discriminator NVARCHAR(100) (NOT NULL)
IsActive BIT (NOT NULL)
PhoneNumber NVARCHAR(50) (NULLABLE)
HomeRoom NVARCHAR(50) (NULLABLE)

数据可能看起来像这样:

Id Discriminator IsActive PhoneNumber HomeRoom
1 Teacher 1 NULL 2A
2 Administrator 1 3256-6986 NULL

在这种情况下,您的职责表将具有EmployeeID 的 FK,它将加入此表。从概念上讲,它很简单,但是您必须确保在对特定类型进行查询时始终添加鉴别器。使用 EF Core,这主要是为您处理的。

如果您想查询所有教师,您可以执行如下查询:

SELECT * FROM Employee WHERE Discriminator = 'Teacher'

这是 EF Core 中默认方式的原因是因为它允许更快地查询数据。关于这 1 类型的所有内容都存储在一个表中。这种好处也是它的弱点之一。您可以看到Teacher 行必须存储它的PhoneNumber 为空,即使该数据与教师无关。在这个小样本中这没问题,但是如果您有一堆员工类型,每个类型都有很多独特的列,那么您的表就会增加很多膨胀。这可能会让人很难理解发生了什么。

每种类型的表格

第二种方法是使用table per type。这样,您将拥有一个只有 Id 和 IsActive 的 Employee 表。然后会有一个单独的表供教师/管理员使用,其中包含这些课程的特定数据。

架构可能看起来像这样:

Employee |专栏 |类型 | |---- |-------| |身份证 | NVARCHAR(50) PK 标识 | |活跃 |位(非空)|

Administrator |专栏 |类型 | |---- |-------| |员工 ID | NVARCHAR(50) PK | |电话号码 | NVARCHAR(50) (NULLABLE) |

Teacher |专栏 |类型 | |---- |-------| |员工 ID | NVARCHAR(50) PK | |主页房间 | NVARCHAR(50) (NULLABLE) |

显示数据的样子有点困难,但基本上你会让每个员工都在员工表中。那么如果他们是老师,Teacher 表中就会有一条与 EmployeeId 匹配的记录。

所以如果你想用这个方法查询所有的老师,你可以这样做:

SELECT * FROM Employee
INNER JOIN Teacher ON Teacher.EmployeeId = Employee.Id

这种方式的好处是您不会存储任何浪费的数据。

不利的一面是,您可能会通过添加大量联接来使查询变得更加复杂。 In the EF Core docs they mention this performance hit.

另类

我在开头提到我会尝试按原样回答您的问题,我已经这样做了。但我也认为提供可能适用也可能不适用的替代方案很重要。对我来说,您似乎是从 OOP 方法开始希望数据的外观,然后您会问“我如何将其映射到数据库”。但是,OOP 设计并不能真正 100% 使用模式,因此请从模式开始,然后弄清楚如何用类来表示它。

考虑以下问题:员工是否可以同时担任多个角色?使用您当前的系统,某人被锁定为一种类型的员工。如果有人换了角色怎么办?也许他们从管理员开始,但变成了老师?我并不是说这绝对是一个问题。也许您会考虑将某人换工作作为新员工,在这种情况下,您当前的方式会奏效。

我建议以完全不同的方式构建它。让您的Employee 表包含所有员工可能需要的所有信息(无论他们担任什么角色)。然后有一个单独的表,名为Role,其中包含所有可能的不同类型员工的行。例如,它会有一行代表“教师”、“管理员”、“场地管理员”等。这样添加新角色就像在表格中添加一行一样简单。

然后你会有一个名为EmployeeRole 的多对多表,它只存储EmployeeIDRoleID(也许还有一个IsActive 来说明他们是否还在那个角色中)。这样,一名员工可以担任多个角色,并且您可以了解他们之前担任过的角色的历史记录。

然后你可以让你的Duty 表有一个到这个EmployeeRole 表的 FK,而不是直接到 Employee 表。员工拥有的每个角色都会有一组不同的职责。

虽然我不是数据库设计师,但这种设计绝对不是没有自己的缺陷,因此请考虑您的用例并仔细设计您需要允许的限制。不要用 OOP 术语来考虑你的代码,从你的模式中创建一个类结构。用我的例子来换一种方式思考这个问题,但不要盲目地遵循我的建议。

总结

如果您想坚持原来的设计,我认为 Table-Per-Hierarchy 最适合您。但我强烈建议您重新考虑您的架构并弄清楚适用于它的限制类型,以及您是否需要考虑这些限制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-05
    • 2014-11-07
    • 1970-01-01
    • 1970-01-01
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 2012-05-04
    相关资源
    最近更新 更多