Why EF Core keeps adding shadow properties to migration files? Like AgencyId1, AgencyId2 etc

Viewed 81

When I try to create a migration EF keeps adding shadow properties like AgencyId, AgencyId1, AgencyId2. I think the issue is in my configs but I've tried several approaches and none of them worked

Generates stuff like that:

  migrationBuilder.CreateTable(
            name: "Packages",
            columns: table => new
            {​
                AgencyId1 = table.Column<int>(type: "int", nullable: true),
                AgencyId = table.Column<int>(type: "int", nullable: false)
            }​,
            constraints: table =>
            {​
                table.PrimaryKey("PK_Packages", x => x.Id);
                table.ForeignKey(
                    name: "FK_Packages_Agencies_AgencyId",
                    column: x => x.AgencyId,
                    principalTable: "Agencies",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
                table.ForeignKey(
                    name: "FK_Packages_Agencies_AgencyId1",
                    column: x => x.AgencyId1,
                    principalTable: "Agencies",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
            }​);

Package:

  public class Package : PackageModel
{​
    public int AgencyId {​ get; set; }​
    public virtual Agency Agency {​ get; set; }​
    ...

(also tried without virtual keyword)

AgencyConfiguration

  internal class AgencyConfiguration : IEntityTypeConfiguration<Agency>
{​
    public void Configure(EntityTypeBuilder<Agency> builder)
    {​
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id)
            .IsRequired()
            .ValueGeneratedOnAdd();
        builder.Property(x => x.Name)
            .IsRequired()
            .HasMaxLength(250);
        builder.HasMany(x => x.Packages)
            .WithOne()
            .HasForeignKey(x => x.AgencyId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Cascade);
    }​
}​

PackageConfiguration

  internal class PackageConfiguration : IEntityTypeConfiguration<Package>
{​
    public void Configure(EntityTypeBuilder<Package> builder)
    {​
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id)
            .IsRequired()
            .ValueGeneratedOnAdd();
        builder.HasOne(x => x.Agency)
            .WithMany()
            .HasForeignKey(x => x.AgencyId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Cascade);
    }​
}​

(also tried without that duplicated config on Package side, only on Agency - doesn't work as well)

There are no duplicated columns with the same name, no duplicated config files or anything like that.

Configs are being applied in CoreDbContext:

  protected override void OnModelCreating(ModelBuilder modelBuilder)
    {​
        modelBuilder.ApplyConfiguration(new AgencyConfiguration());
        modelBuilder.ApplyConfiguration(new PackageConfiguration());

Any ideas? Thanks

1 Answers

Why EF Core keeps adding shadow properties to migration files? Like AgencyId1, AgencyId2 etc.

The reason is almost always relationship misconfiguration. In your case, here

// Agency
builder.HasMany(x => x.Packages)
    .WithOne() // <-- where is navigation property?
    .HasForeignKey(x => x.AgencyId)
    .IsRequired()
    .OnDelete(DeleteBehavior.Cascade);

// Package
builder.HasOne(x => x.Agency)
    .WithMany() // <-- where is navigation property?
    .HasForeignKey(x => x.AgencyId)
    .IsRequired()
    .OnDelete(DeleteBehavior.Cascade);

First off, this by idea is one relationship, so should be configured in one place. This is where separate entity configuration class concept doesn't play well, because relationship involves two entities, hence does not logically belong to any of them.

So, at least pick one of them and configure it there, but never in two places. Why? Because my omitting navigation property you are actually telling EF Core to create two relationships (with navigation property at only one side), hence the additional FKs.

e.g. either

// Agency
builder.HasMany(x => x.Packages) // collection navigation
    .WithOne(x => a.Agency) // reference navigation
    .HasForeignKey(x => x.AgencyId) // FK property
    .IsRequired() // not really needed, default for non nullable FKs
    .OnDelete(DeleteBehavior.Cascade); // not really needed, default for required relationships

or

// Package
builder.HasOne(x => x.Agency)
    .WithMany(x => x.Packages)
    .HasForeignKey(x => x.AgencyId)
    .IsRequired()
    .OnDelete(DeleteBehavior.Cascade);

never both.

Or even better, since all this is by EF Core conventions, remove the whole relationship configuration.

Related