0

I have got an abstract class called Building

    public abstract class Building
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        ...
    }

and some inheritances:

    public class ManufacturingFacilityBuilding : Building
    {
        public List<InputResource> InputResources { get; set; }
        ...
    }
    public class RefineryBuilding : Building
    {
        public List<InputResource> InputResources { get; set; } = new();
        ...
    }
    public class ResearchLaboratoryBuilding : Building
    {
        public List<InputResource> InputResources { get; set; } = new();
        ...
    }

And the InputResource class:

    public class InputResource
    {
        public Guid Id { get; set; }

        public Guid BuildingId { get; set; }

        public virtual Building Building { get; set; }
    }

The ModelBuilder for the buildings looks like this:

    public class ManufacturingFacilityBuildingEntityTypeConfiguration : IEntityTypeConfiguration<ManufacturingFacilityBuilding>
    {
        public void Configure(EntityTypeBuilder<ManufacturingFacilityBuilding> builder)
        {
            builder.ToTable("ManufacturingFacilityBuildings");
            builder.Property(e => e.Id).IsRequired().HasDefaultValueSql("(newid())");
            builder.HasBaseType<Building>();

            builder.HasMany(e => e.InputResources)
                .WithOne(e => e.Building as ManufacturingFacilityBuilding)
                .HasForeignKey(e => e.BuildingId);
        }
    }
    public class RefineryBuildingEntityTypeConfiguration : IEntityTypeConfiguration<RefineryBuilding>
    {
        public void Configure(EntityTypeBuilder<RefineryBuilding> builder)
        {
            builder.ToTable("RefineryBuildings");
            builder.Property(e => e.Id).IsRequired().HasDefaultValueSql("(newid())");
            builder.HasBaseType<Building>();

            builder.HasMany(e => e.InputResources)
                .WithOne(e => e.Building as RefineryBuilding)
                .HasForeignKey(e => e.BuildingId);
        }
    }
    public class ResearchLaboratoryBuildingEntityTypeConfiguration : IEntityTypeConfiguration<ResearchLaboratoryBuilding>
    {
        public void Configure(EntityTypeBuilder<ResearchLaboratoryBuilding> builder)
        {
            builder.ToTable("ResearchLaboratoryBuildings");
            builder.Property(e => e.Id).IsRequired().HasDefaultValueSql("(newid())");
            builder.HasBaseType<Building>();

            builder.HasMany(e => e.InputResources)
                .WithOne(e => e.Building as ResearchLaboratoryBuilding)
                .HasForeignKey(e => e.BuildingId);
        }
    }

So finally what I expect from the migration is something like this:

migrationBuilder.CreateTable(
                name: "InputResources",
                columns: table => new
                {
                    Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "(newid())"),
                    BuildingId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_InputResources", x => x.Id);
                    table.ForeignKey(
                        name: "FK_InputResources_ManufacturingFacilityBuildings_BuildingId",
                        column: x => x.BuildingId, // <= Watch this property
                        principalTable: "ManufacturingFacilityBuildings",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                    table.ForeignKey(
                        name: "FK_InputResources_RefineryBuildings_BuildingId",
                        column: x => x.BuildingId, // <= Watch this property
                        principalTable: "RefineryBuildings",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                    table.ForeignKey(
                        name: "FK_InputResources_ResearchLaboratoryBuildings_BuildingId",
                        column: x => x.BuildingId, // <= Watch this property
                        principalTable: "ResearchLaboratoryBuildings",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

The InputResource should have it's FK directly on BuildingId. But when I create the migration, it generates something like this:

migrationBuilder.CreateTable(
                name: "InputResources",
                columns: table => new
                {
                    Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "(newid())"),
                    BuildingId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
                    ManufacturingFacilityBuildingId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
                    RefineryBuildingId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_InputResources", x => x.Id);
                    table.ForeignKey(
                        name: "FK_InputResources_ManufacturingFacilityBuildings_ManufacturingFacilityBuildingId",
                        column: x => x.ManufacturingFacilityBuildingId, // <= This is where the problem begins
                        principalTable: "ManufacturingFacilityBuildings",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                    table.ForeignKey(
                        name: "FK_InputResources_RefineryBuildings_RefineryBuildingId",
                        column: x => x.RefineryBuildingId, // <= This is where the problem begins
                        principalTable: "RefineryBuildings",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                    table.ForeignKey(
                        name: "FK_InputResources_ResearchLaboratoryBuildings_BuildingId",
                        column: x => x.BuildingId, // <= This is as it should be
                        principalTable: "ResearchLaboratoryBuildings",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

As you can see the migration creates a custom column foreach (except one) building. Is this intended or am I doing something wrong here?

I am using .Net 5 with EF-Core 5.0

DirtyNative
  • 2,553
  • 2
  • 33
  • 58
  • As you are using a different table for each different building type, I don't think you can use the same column as foreign key to multiple tables. at the database level you simply won't be able to know which table is being referenced by the `BuildingId` column, check this sources: [source1](https://stackoverflow.com/questions/668921/foreign-key-referring-to-primary-keys-across-multiple-tables) [source2](https://stackoverflow.com/questions/52411193/foreign-key-to-multiple-tables-at-the-same-time) – Modar Na Apr 14 '21 at 22:28
  • I am unsure if it is really not possible.. I thought I have seen something similar once. Also if I edit the migration by hand and apply it to the database, it works as expected – DirtyNative Apr 15 '21 at 10:32
  • [See this example](https://blog.jetbrains.com/dotnet/2020/11/25/getting-started-with-entity-framework-core-5/) that it should work abstract classes and one to many relationships – DirtyNative Apr 16 '21 at 08:37
  • I can't understand how the database server will be ok when inserting a value in `BuildingId` that is present in `RefineryBuildings` but not in `ResearchLaboratoryBuildings`, wouldn't that throw a constraint exception – Modar Na Apr 18 '21 at 18:53

0 Answers0