dotnet/efcore · error · InvalidOperationException

The foreign keys on ' ' and on ' ' are both mapped to ' …

Error message

The foreign keys {foreignKeyProperties1} on '{entityType1}' and {foreignKeyProperties2} on '{entityType2}' are both mapped to '{table}.{foreignKeyName}', but referencing different principal columns ({principalColumnNames1} and {principalColumnNames2}).

What it means

Thrown during relational model finalization when two foreign keys resolve to the same constraint name on the same table but their principal key properties map to different principal columns. EF requires every constraint sharing a name to be byte-for-byte identical so that migrations emit a single, unambiguous constraint.

Solutions

  1. Inspect the constraint name in the message and give the two FKs distinct names via HasConstraintName(...) so they no longer collide.
  2. Align both FKs to reference the same principal key (same PrincipalKey) so the principal columns match.
  3. If the duplication is intentional (e.g. table-splitting), remap one FK to a different column set or remove the redundant FK.
  4. Use ToTable/ToView to separate the entities onto different tables so their constraints do not share a name.

Example fix

// before: both FKs default to the same constraint name but target different principal columns
modelBuilder.Entity<Order>().HasOne(o => o.Customer).WithMany().HasForeignKey(o => o.CustomerId);
modelBuilder.Entity<Invoice>().HasOne(i => i.Customer).WithMany().HasForeignKey(i => o.CustomerAltId);

// after: give them explicit, distinct constraint names
modelBuilder.Entity<Order>().HasOne(o => o.Customer).WithMany().HasForeignKey(o => o.CustomerId)
    .HasConstraintName("FK_Order_Customer_Id");
modelBuilder.Entity<Invoice>().HasOne(i => i.Customer).WithMany().HasForeignKey(i => i.CustomerAltId)
    .HasConstraintName("FK_Invoice_Customer_AltId");
Defensive patterns

Strategy: validation

Validate before calling

// Before finalizing the model, ensure colliding FKs share principal columns.
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    foreach (var fk in entityType.GetForeignKeys())
    {
        var name = fk.GetConstraintName();
        var dupes = modelBuilder.Model.GetEntityTypes()
            .SelectMany(e => e.GetForeignKeys())
            .Where(o => o != fk && o.GetConstraintName() == name)
            .ToList();
        foreach (var dup in dupes)
        {
            if (!fk.PrincipalKey.Properties.SequenceEqual(dup.PrincipalKey.Properties))
                Console.WriteLine($"FK constraint name '{name}' collides with different principal columns.");
        }
    }
}

Prevention

When it happens

Trigger: RelationalForeignKeyExtensions.AreCompatible(foreignKey, duplicateForeignKey, storeObject, shouldThrow: true) when !principalColumns.SequenceEqual(duplicatePrincipalColumns). Happens when two entities share a table (TPT/TPH/splitting) and their FKs land on the same constraint name while pointing at different principal key columns (e.g. one targets PK.Id, another targets an alternate key on a different column).

Common situations: TPT inheritance where derived entities define FKs that collapse to the same default constraint name; table-splitting where the same principal is referenced through different keys; manual HasConstraintName calls that collide; upgrade from a version that did not validate this to one that does.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/26f83430d76c3814. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs:101

        {
            return shouldThrow
                ? throw new InvalidOperationException(
                    RelationalStrings.DuplicateForeignKeyColumnMismatch(
                        foreignKey.Properties.Format(),
                        foreignKey.DeclaringEntityType.DisplayName(),
                        duplicateForeignKey.Properties.Format(),
                        duplicateForeignKey.DeclaringEntityType.DisplayName(),
                        foreignKey.DeclaringEntityType.GetSchemaQualifiedTableName(),
                        foreignKey.GetConstraintName(storeObject, principalTable.Value),
                        foreignKey.Properties.FormatColumns(storeObject),
                        duplicateForeignKey.Properties.FormatColumns(storeObject)))
                : false;
        }

        if (!principalColumns.SequenceEqual(duplicatePrincipalColumns))
        {
            return shouldThrow
                ? throw new InvalidOperationException(
                    RelationalStrings.DuplicateForeignKeyPrincipalColumnMismatch(
                        foreignKey.Properties.Format(),
                        foreignKey.DeclaringEntityType.DisplayName(),
                        duplicateForeignKey.Properties.Format(),
                        duplicateForeignKey.DeclaringEntityType.DisplayName(),
                        foreignKey.DeclaringEntityType.GetSchemaQualifiedTableName(),
                        foreignKey.GetConstraintName(storeObject, principalTable.Value),
                        foreignKey.PrincipalKey.Properties.FormatColumns(principalTable.Value),
                        duplicateForeignKey.PrincipalKey.Properties.FormatColumns(principalTable.Value)))
                : false;
        }

        if (foreignKey.IsUnique != duplicateForeignKey.IsUnique)
        {
            return shouldThrow
                ? throw new InvalidOperationException(
                    RelationalStrings.DuplicateForeignKeyUniquenessMismatch(
                        foreignKey.Properties.Format(),

View on GitHub (pinned to 3a2006ef56)