ASP.NET 核心 2:在这种情况下,"many-to-one" 关系背后的代码可能是什么?

ASP.NET Core 2: What could be the code behind a "many-to-one" relationship in this case?

我正在 ASP .NET Core 2 应用程序中准备项目的数据结构 (code-first) , 在 Entity Framework 的帮助下。我没有经验的这种特定关系:用户必须能够通过复选框选择疾病,我们有类似的选择:癌症类型、饮食等。

我有两个以上像图上的table,会从UserKitProperties table中引用。这个 table 应该像连接器 table 一样工作,将用户实体与其他实体连接起来。

userid1 | cancertypeid1
userid2 | dietaryid1
userid1 | cancertypeid2
userid3 | dietaryid1

这应该如何在代码中指定,以支持这种关系?我正在考虑做一个基础 class 并可能参考那个 id。这是连接器 class..

public class PatientProperties : EntityModel
    {
        [Key]
        public long ID { get; set; }

        public long PatientID { get; set; }

        [ForeignKey("PatientID")]
        public Patient Patients { get; set; }

        // this should be used for cancer type, dietary, etc..
        public long PropertyID { get; set; }

        /* Instead of using two classes' ids, maybe call the base class' id
        [ForeignKey("PropertyID")]
        public CancerType CancerTypes { get; set; }

        [ForeignKey("PropertyID")]
        public Dietary Dietaries { get; set; } */
    }

提前感谢您的建议! :)

以下应该有效:

public class Property 
{
    public long PropertyId { get; set; }
}

public class CancerType : Property 
{ 
    // Your code
}

public class Dietary : Property 
{   
    // Your code
}

public class PatientProperties : EntityModel
{
    [Key]
    public long ID { get; set; }

    public long PatientID { get; set; }

    [ForeignKey("PatientID")]
    public Patient Patients { get; set; }

    public long PropertyID { get; set; }

    [ForeignKey("PropertyID")]
    public Property Property { get; set; }
}

但是正如这位 MS doc 提到的,设置这种继承将使用特殊的 Discriminator base class table 中的列,以表示一行中存储的特定类型。

我个人会使用可空字段来避免增加复杂性。但是,这并不强制要求 PatientProperties 只有一个 属性,这是一个相当大的减号:

public class PatientProperties : EntityModel
{
    [Key]
    public long ID { get; set; }

    public long PatientID { get; set; }

    [ForeignKey("PatientID")]
    public Patient Patients { get; set; }

    public long? CancerTypeID { get; set; }
    [ForeignKey("CancerTypeID")]
    public CancerType CancerType { get; set; }

    public long? DietaryID { get; set; }
    [ForeignKey("DietaryID")]
    public Dietary Dietary { get; set; }
}

与其首先考虑数据库布局,不如考虑如何在代码中表示这种关系。毕竟,您采用的是代码优先方法。

基本上有两种选择:患者有多个属性,每个 属性 类型一个,或者所有属性只有一个集合:

public class Patient
{
    // …

    // option 1
    public CancerType CancerType { get; set; }
    public Dietary Dietary { get; set; }
    public OtherProperty OtherProperty { get; set; }

    // option 2
    public IList<PatientProperty> Properties { get; set; }
}

这两种选择各有优缺点。虽然选项 1 非常明确并且为每种类型强制执行单个值,但它还要求您为每个(患者)属性 设置一个 (class) 属性。所以如果你以后扩展你的模型,你将不得不调整你的患者模型。

方案2的好处是什么都能收。因此,如果您引入新属性,您可以只为您的患者添加属性,而无需稍后修改模型。另外,它也直接支持单一种类的多选。不利的一面是,它不会自行验证任何内容,因此您需要业务逻辑来实际执行您的规则。


转到数据库,对于选项 2,您显然需要 link table,因为现在这是多对多关系。由于您只有 link 到基本类型 PatientProperty 但您实际上想谈论具体类型,因此您将需要某种 鉴别器 。鉴别器基本上只是一种符号,用于在数据库中额外存储对象的种类

用继承存储数据时,通常做的是“table-per-hierarchy”。这意味着 PatientProperty 基类型层次结构中的所有类型将共享相同的 table。鉴别器列用于指定类型,某些 属性 类型可能具有的其他属性是使用可为空的列实现的。此设置与 Entity Framework 开箱即用,并在 in this chapter in the documentation.

中进行了描述

另一种方法“table-per-type”在 EF Core 中不受支持,因此如果您想遵循该方法,则必须自己实施。但在你的情况下,属性 类型大多非常相似,我实际上会反对这一点并实际上将它们保持在相同的 table.

对于选项 1,只要每种类型的 属性 分配给患者,事情就会简单一些。因为那里没有多对多,所以你实际上不需要 link table。您只需要在患者模型中存储每个 linked 属性 类型的 ID,如上面的 UML 所示。这样做,您还可以将 属性 类型保留为不在数据库中共享单个 table 的单独类型。