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
- Inspect the constraint name in the message and give the two FKs distinct names via HasConstraintName(...) so they no longer collide.
- Align both FKs to reference the same principal key (same PrincipalKey) so the principal columns match.
- If the duplication is intentional (e.g. table-splitting), remap one FK to a different column set or remove the redundant FK.
- 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
- Always set explicit HasConstraintName for FKs in TPT/splitting scenarios to avoid accidental name collisions.
- Run model validation in a unit test (modelBuilder.FinalizeModel()) as part of CI.
- Keep one relationship per (principal, dependent) pair unless you intentionally differentiate via constraint names.
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
- The foreign keys on ' ' and on ' ' are both mapped to ' …
- The foreign keys on ' ' and on ' ' are both mapped to ' …
- The foreign keys on ' ' and on ' ' are both mapped to ' …
- The indexes on ' ' and on ' ' are both mapped to ' ', but…
- The keys on ' ' and on ' ' are both mapped to ' ', but on…
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)