简体   繁体   中英

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

I'm preparing a project's data structure ( code-first ) in an ASP .NET Core 2 application, with the help of Entity Framework . This specific relationship I have no experience with: the user has to be able to choose diseases with checkboxes, and we have similar choices: cancer type, dietary, etc..

I have more than two tables like the ones on the picture, which will be referred from the UserKitProperties table. This table should work like a connector table, connects the user entity with other entities.

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

在此输入图像描述

How should this be specified in the code, to support this relationship? I was thinking on doing a base class and maybe refer to that id. And this is the connector 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; } */
    }

Thank you in advance for your suggestions! :)

The following should work:

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; }
}

But as this MS doc mentions, setting up such inheritence will use a special Discriminator column in the base class table, to represent what specific type is stored in a row.

I personally would resort to having nullable fields instead in order to not add more complexity. This doesn't enforce, however, that PatientProperties only has one property, which is a considerable minus:

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; }
}

Instead of thinking about the database layout first, you should think about how you would represent this relationship in code. After all, you are doing a code-first approach.

There are basically two choices you could choose: Either the patient has multiple properties, one for each property type, or there is just a single collection for all properties:

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; }
}

Both of these options have their advantages and disadvantages. While option 1 is very explicit and enforces a single value for every type, it also requires you to have a (class) property for every (patient) property. So if you extend your model later, you will have to adjust your patient model.

选项1

Option 2 has the benefit that it can just collect everything. So you can just add properties to your patient without having to modify the model later if you introduce new properties. In addition, it would also directly support multiple selections for a single kind. On the downside, it does not verify anything on its own, so you need business logic to actually enforce your rules.

选项2


Moving onto the database, for option 2 you obviously need a link table since that is a many-to-many relationship now. Since you only have a link to the base type PatientProperty but you actually want to talk about the concrete type, you will need some kind of discriminator . Discriminators are basically just a notation to additionally store the kind of object in the database.

When storing data with inheritance, what is commonly done is “table-per-hierarchy”. That means that all types within the hierarchy of the PatientProperty base type will share the same table. A discriminator column is used to specify the type, and additional properties that some property types may have are implemented with nullable columns. This setup works out of the box with Entity Framework and is described in this chapter in the documentation .

The other approach, “table-per-type” is not supported in EF Core, so if you wanted to follow that, you would have to implement it yourself. But in your case, where the property types are mostly very similar, I would actually argue against that and actually keep them in the same table.

For option 1, as long as you only have a single property of each kind assigned to the patient, things are a bit simpler. Since you don't have many-to-many there, you don't actually need a link table. You just need to store the id for each linked property type in the patient model, as shown in the above UML. Doing that, you can also keep the property types as separate types that do not share a single table in the database.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM