dotnet/efcore · error · InvalidOperationException

The keys {keyProperties1} on '{entityType1}' and {keyPropert

Error message

The keys {keyProperties1} on '{entityType1}' and {keyProperties2} on '{entityType2}' are both mapped to '{keyName}', but on different tables ('{table1}' and '{table2}').

What it means

Thrown by RelationalKeyExtensions.AreCompatible during model validation when two keys (PK or alternate) share the same key name but key.Properties.GetColumnNames(storeObject) returns null for at least one — meaning the key's properties do not resolve to columns on the store object the duplicate lives on. EF treats same-named keys as one physical constraint, so it refuses keys that do not share one concrete column set on one table.

Source

Thrown at src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs:33

{
    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public static bool AreCompatible(
        this IReadOnlyKey key,
        IReadOnlyKey duplicateKey,
        in StoreObjectIdentifier storeObject,
        bool shouldThrow)
    {
        var columnNames = key.Properties.GetColumnNames(storeObject);
        var duplicateColumnNames = duplicateKey.Properties.GetColumnNames(storeObject);
        return columnNames == null
            || duplicateColumnNames == null
                ? shouldThrow
                    ? throw new InvalidOperationException(
                        RelationalStrings.DuplicateKeyTableMismatch(
                            key.Properties.Format(),
                            key.DeclaringEntityType.DisplayName(),
                            duplicateKey.Properties.Format(),
                            duplicateKey.DeclaringEntityType.DisplayName(),
                            key.GetName(storeObject),
                            key.DeclaringEntityType.GetSchemaQualifiedTableName(),
                            duplicateKey.DeclaringEntityType.GetSchemaQualifiedTableName()))
                    : false
                : columnNames.SequenceEqual(duplicateColumnNames)
                || (shouldThrow
                    ? throw new InvalidOperationException(
                        RelationalStrings.DuplicateKeyColumnMismatch(
                            key.Properties.Format(),
                            key.DeclaringEntityType.DisplayName(),
                            duplicateKey.Properties.Format(),
                            duplicateKey.DeclaringEntityType.DisplayName(),
                            key.DeclaringEntityType.GetSchemaQualifiedTableName(),

View on GitHub (pinned to dbf9771522)

Solutions

  1. Rename one key so each table/constraint has a distinct name, or remove the explicit key-name override.
  2. Ensure both entity types actually share the same table and that every key property resolves to a column on it (avoid JSON mapping for the colliding key).
  3. If one entity is JSON-mapped, do not give its key the same name as a table-mapped entity's key.
  4. Inspect key.Properties.GetColumnNames(storeObject) on both keys to see which returns null and why.

Example fix

// before
modelBuilder.Entity<Tenant>().HasAlternateKey(t => t.Code).HasName("AK_Code");
modelBuilder.Entity<Region>().HasAlternateKey(r => r.Code).HasName("AK_Code"); // different tables -> throws

// after
modelBuilder.Entity<Region>().HasAlternateKey(r => r.Code).HasName("AK_Region_Code");
Defensive patterns

Strategy: validation

Validate before calling

// Ensure each named key resolves columns on a single table
foreach (var et in model.GetEntityTypes())
{
    foreach (var key in et.GetDeclaredKeys())
    {
        foreach (var mapping in et.GetTableMappings())
        {
            var so = StoreObjectIdentifier.Table(mapping.Table.Name, mapping.Table.Schema);
            if (key.GetName(so) is string n
                && key.Properties.GetColumnNames(so) is null)
            {
                throw new InvalidOperationException($"Key '{n}' on {et.Name} does not resolve to columns on {so.DisplayName()}");
            }
        }
    }
}

Prevention

When it happens

Trigger: Lines 30-42 of RelationalKeyExtensions.cs: columnNames == null || duplicateColumnNames == null. Produced when HasKey/HasAlternateKey with GetName(storeObject) collides across entity types whose properties are not mapped to columns on the shared table (e.g. JSON-mapped entities at line 109, or TPT table mismatches).

Common situations: Two entity types configured with the same key name via the Name annotation but mapped to different tables; an entity mapped to JSON getting a key whose name collides with a table-mapped entity's key; TPT inheritance where a key name is reused across hierarchy tables.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/1ccc47234be4777d. Report an issue: GitHub.