使用继承时复制外键 [英] Duplicate ForeignKey when using inheritance

查看:46
本文介绍了使用继承时复制外键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经创建了这些类,以便通过EntityFramework 6代码优先方法生成数据库模型:

I have created these classes in order to generate the database model via EntityFramework 6 code-first approach:

public class Vehicle 
{
    public long Id { get; set; }

    public long ResponsiblePersonId { get; set; }
}

public class Car: Vehicle {

    public int HorsePower { get; set; }

}

public class Bike: Vehicle {

    public int FrameSize { get; set; }

}

public class Organisation
{
    public Organisation()
    {
        Cars = new List<Car>();
        Bikes = new List<Bikes>();
    }

    public long Id { get; set; }


    public List<Car> Cars { get; set; }

    public List<Bike> Bikes { get; set; }
}

到目前为止,这对我来说似乎是正确的. 但是不幸的是,结果表看起来像这样:

So far this seemed right for me. But unfortunately, the resulting table looks like this:

Id | ResponsiblePersonId | HorsePower | FrameSize | Discriminator | Organisation_Id | Organisation_Id1

为什么组织ForeignKey生成两次?我希望该表只有一个Organisation_Id列.

Why is the Organisation ForeignKey being generated twice? I expected this table to only have one Organisation_Id column.

谢谢

推荐答案

EF通过多种方式为继承层次结构实现物理表.默认的一种(即您正在使用的)称为每个层次的表(TPH)".它仅对所有派生实体使用一张表,并使用一个Discriminator列指定记录中包含的实体的类型. EF还在表中为任何派生实体中包含的每个属性添加了一列.

There are several ways for EF to implement the physical tables for your inheritance hierarchy. The default one, the one you are using, is called Table Per Hierarchy (TPH). It uses only one table for all the derived entities, with one Discriminator column to specify the type of entity which is contained in the record. EF also adds to the table a column for each property that is included in any of the derived entities.

因此,在子级定义了派生实体与Organisation之间的关系(Organisation实体中的CarBike属性的列表),EF决定为每种子实体类型创建一个单独的列Organisation_Id,而您不想那样.

So as the relationship between the derived entities and Organisation is defined at child level (the lists of Car and Bike properties in Organisation entity) EF decides to create a separate column for each child entity type Organisation_Id, and you don't want that.

该如何更改?有几种方法:

How to change this? There are several ways:

  • 不使用TPH.改用TPC(每个具体的表类).也就是说,EF为您的每个子实体创建一个单独的表.操作方法:从DbContext中删除DbSet<Vehicle>属性.如果这样做没有成功,请为从Vehicle派生的每个实体的物理表名称设置一个明确的配置,如下所示:

  • Don't use TPH. Use instead TPC (Table Per Concrete class). That is, EF creates a separate table for each one of your child entities. How to do this: remove the DbSet<Vehicle> property from your DbContext. If this doesn't make it, set an explicit configuration for the physical table name for each entity derived from Vehicle like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    ...
    modelBuilder.Entity<Car>().ToTable("Cars");
    modelBuilder.Entity<Bike>().ToTable("Bikes");
}

  • 如果您需要继续使用TPH,我不知道有什么方法可以实现,它只会在数据库中生成一个OrganisationId列,并且在VehicleOrganisation之间仅生成一个外键. .常识会说您可以在Vehicle基本实体级别定义Organisation外键.但是随后在生成迁移时会出现错误:

  • If you need to continue using TPH, I don't know of any way to implementing this that will generate only one OrganisationId column in the database and only one Foreign Key between Vehicle and Organisation. Common sense would say that you might define the Organisation foreign key at the Vehicle base entity level. But then you get errors when generating the migration:

    组织:FromRole:NavigationProperty'组织'不是 有效的.在中输入FromRole'Organisation_Cars_Target'的'Car' AssociationType'Organisation_Cars'必须与类型完全匹配 在其上声明了此NavigationProperty的车辆".

    Organisation: FromRole: NavigationProperty 'Organisation' is not valid. Type 'Car' of FromRole 'Organisation_Cars_Target' in AssociationType 'Organisation_Cars' must exactly match with the type 'Vehicle' on which this NavigationProperty is declared on.

    似乎,当在基础级别定义关系时,EF期望Organisation中的列表定义为Vehicle类型,而不是CarBike类型.这不适合您的模型.

    It seems that when the relationship is defined at base level then EF expects the lists in Organisation to be defined of type Vehicle and not Car or Bike. And this does not fit with your model.

    如果您尝试在派生类中定义OrganisationIdOrganisation属性,则在生成迁移时会遇到不同的错误,因为不允许在不同的子实体中对这些属性使用相同的名称.您可以使用不同的名称,但随后会再次得到两列.也无法以这种方式获得一列.

    And if you try to define OrganisationId or Organisation properties in your derived classes then you get a different error when generating the migration, because you are not allowed to use the same name for the properties in the different child entities. You can use different names, but then you get two columns again. There is no way to get one column this way either.

    因此,据我所知,如果您坚持使用TPH,则必须忍受OrganisationId有两列.至少您可以使用一些流畅的配置以更详细的方式命名它们:

    So if you stick with TPH, as far as I know, you have to put up with having two columns for your OrganisationId. At least you can name them in a more verbose way with some fluent configurations:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        ...
        modelBuilder.Entity<Organisation>()
            .HasMany(o => o.Bikes)
            .WithRequired()
            .Map(x => x.MapKey("OrganisationIdBike"));
    
        modelBuilder.Entity<Organisation>()
            .HasMany(o => o.Cars)
            .WithRequired()
            .Map(x => x.MapKey("OrganisationIdCar"));
    }
    

  • 我建议您改用TPC,因为对于您的模型而言,流畅的映射的编写要简单一些.

    I would recommend you to change to TPC, as with your model the fluent mappings are a bit less complex to write.

    要更好地了解TPH,TPC和TPT(每种类型的表,又是继承层次结构的另一种实现),请阅读

    For a better understanding of TPH, TPC and TPT (Table Per Type, yet another implementation of inheritance hierarchies) read this post.

    这篇关于使用继承时复制外键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆