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 '{foreignKeyName}', but are declared on different tables ('{table1}' and '{table2}').

What it means

Two foreign keys resolve to the same constraint name in the database but their declaring entity types map to different tables, so they cannot share a single constraint. RelationalForeignKeyExtensions.AreCompatible throws DuplicateForeignKeyTableMismatch when GetColumnNames fails for either FK on the store object (i.e. they live on different tables).

Solutions

  1. Let EF auto-generate constraint names, or ensure any HasConstraintName override is unique per physical table.
  2. Inspect both foreign keys shown in the message and confirm whether they really should share a name (they should not, since they are on different tables).
  3. Rename one constraint so the database-level names diverge.

Example fix

// before
modelBuilder.Entity<Order>().HasOne(o => o.Customer).WithMany()
    .HasForeignKey(o => o.CustomerId).HasConstraintName("FK_Customer");
modelBuilder.Entity<Invoice>().HasOne(i => i.Customer).WithMany()
    .HasForeignKey(i => i.CustomerId).HasConstraintName("FK_Customer");

// after
modelBuilder.Entity<Invoice>().HasOne(i => i.Customer).WithMany()
    .HasForeignKey(i => i.CustomerId).HasConstraintName("FK_Invoice_Customer");
Defensive patterns

Strategy: validation

Validate before calling

// Ensure no two FKs on different tables share a constraint name
var groups = modelBuilder.Model.GetEntityTypes()
    .SelectMany(e => e.GetDeclaredForeignKeys())
    .Where(fk => fk.GetConstraintName() is not null)
    .GroupBy(fk => (fk.GetConstraintName(), fk.DeclaringEntityType.GetSchemaQualifiedTableName()));
foreach (var g in groups.Where(g => g.Count() > 1))
{
    // Names must be unique per table — but here the conflict is across tables sharing a name
}
// Stronger: assert each constraint name maps to exactly one table
var nameToTables = modelBuilder.Model.GetEntityTypes()
    .SelectMany(e => e.GetDeclaredForeignKeys())
    .Where(fk => fk.GetMappedForeignKeys(StoreObjectIdentifier.Table(fk.DeclaringEntityType.GetTableName()!)).Any())
    .GroupBy(fk => fk.GetConstraintName())
    .Where(g => g.Select(fk => fk.DeclaringEntityType.GetSchemaQualifiedTableName()).Distinct().Count() > 1);
if (nameToTables.Any()) throw new InvalidOperationException("A FK constraint name is shared across different tables.");

Prevention

When it happens

Trigger: Two entities on different tables each have a foreign key whose computed name collides (because EF generates names from properties + principal); overriding HasConstraintName to the same value on FKs declared on different tables.

Common situations: Manually forcing the same constraint name across two unrelated FKs; TPT/split-table scenarios where two entity types accidentally resolve to the same FK name but on different tables; legacy naming overrides that no longer fit after a schema split.

Related errors


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

Appendix: source

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

        bool shouldThrow)
    {
        var principalType = foreignKey.PrincipalKey.IsPrimaryKey()
            ? foreignKey.PrincipalEntityType
            : foreignKey.PrincipalKey.DeclaringEntityType;
        var principalTable = StoreObjectIdentifier.Create(principalType, storeObject.StoreObjectType);

        var duplicatePrincipalType = duplicateForeignKey.PrincipalKey.IsPrimaryKey()
            ? duplicateForeignKey.PrincipalEntityType
            : duplicateForeignKey.PrincipalKey.DeclaringEntityType;
        var duplicatePrincipalTable = StoreObjectIdentifier.Create(duplicatePrincipalType, storeObject.StoreObjectType);

        var columnNames = foreignKey.Properties.GetColumnNames(storeObject);
        var duplicateColumnNames = duplicateForeignKey.Properties.GetColumnNames(storeObject);
        if (columnNames is null
            || duplicateColumnNames is null)
        {
            return shouldThrow
                ? throw new InvalidOperationException(
                    RelationalStrings.DuplicateForeignKeyTableMismatch(
                        foreignKey.Properties.Format(),
                        foreignKey.DeclaringEntityType.DisplayName(),
                        duplicateForeignKey.Properties.Format(),
                        duplicateForeignKey.DeclaringEntityType.DisplayName(),
                        principalTable.HasValue
                            ? foreignKey.GetConstraintName(storeObject, principalTable.Value)
                            : foreignKey.GetDefaultName(),
                        foreignKey.DeclaringEntityType.GetSchemaQualifiedTableName(),
                        duplicateForeignKey.DeclaringEntityType.GetSchemaQualifiedTableName()))
                : false;
        }

        if (principalTable is null
            || duplicatePrincipalTable is null
            || principalTable != duplicatePrincipalTable
            || foreignKey.PrincipalKey.Properties.GetColumnNames(principalTable.Value)
                is not { } principalColumns

View on GitHub (pinned to 3a2006ef56)