dotnet/efcore · error · InvalidOperationException

The number of key column types ({typesCount}) doesn't match

Error message

The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data modification operation on '{table}'. Provide the same number of key column types and key columns.

What it means

Thrown inside GenerateModificationCommands(UpdateDataOperation, ...) in src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs:1082 (message key UpdateDataOperationKeyTypesCountMismatch). It throws InvalidOperationException when operation.KeyColumnTypes is non-null but its length differs from operation.KeyColumns.Length. KeyColumnTypes is an optional type override for the WHERE-clause key columns; if supplied it must align element-for-element with KeyColumns.

Source

Thrown at src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs:1082

        if (operation.Columns.Length != operation.Values.GetLength(1))
        {
            throw new InvalidOperationException(
                RelationalStrings.UpdateDataOperationValuesCountMismatch(
                    operation.Values.GetLength(1), operation.Columns.Length, FormatTable(operation.Table, operation.Schema)));
        }

        if (operation.KeyValues.GetLength(0) != operation.Values.GetLength(0))
        {
            throw new InvalidOperationException(
                RelationalStrings.UpdateDataOperationRowCountMismatch(
                    operation.Values.GetLength(0), operation.KeyValues.GetLength(0), FormatTable(operation.Table, operation.Schema)));
        }

        if (operation.KeyColumnTypes != null
            && operation.KeyColumns.Length != operation.KeyColumnTypes.Length)
        {
            throw new InvalidOperationException(
                RelationalStrings.UpdateDataOperationKeyTypesCountMismatch(
                    operation.KeyColumnTypes.Length, operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));
        }

        if (operation.ColumnTypes != null
            && operation.Columns.Length != operation.ColumnTypes.Length)
        {
            throw new InvalidOperationException(
                RelationalStrings.UpdateDataOperationTypesCountMismatch(
                    operation.ColumnTypes.Length, operation.Columns.Length, FormatTable(operation.Table, operation.Schema)));
        }

        if (operation.KeyColumnTypes == null
            && model == null)
        {
            throw new InvalidOperationException(
                RelationalStrings.UpdateDataOperationNoModel(
                    FormatTable(operation.Table, operation.Schema)));

View on GitHub (pinned to dbf9771522)

Solutions

  1. Make operation.KeyColumnTypes either null (types resolved from the model) or an array of exactly operation.KeyColumns.Length.
  2. Re-scaffold the migration to regenerate consistent keyColumnTypes.
  3. Map the table to an entity type so keyColumnTypes can be omitted.
  4. Audit: for every i, KeyColumns[i] must pair with KeyColumnTypes[i].

Example fix

// before (2 key columns, 1 key type):
migrationBuilder.UpdateData(
    table: "Members",
    keyColumns: new[] { "UserId", "GroupId" },
    keyColumnTypes: new[] { "int" },
    keyValues: new object[,] { { 7, 3 } },
    columns: new[] { "Role" },
    values: new object[,] { { "Admin" } });

// after (aligned):
migrationBuilder.UpdateData(
    table: "Members",
    keyColumns: new[] { "UserId", "GroupId" },
    keyColumnTypes: new[] { "int", "int" },
    keyValues: new object[,] { { 7, 3 } },
    columns: new[] { "Role" },
    values: new object[,] { { "Admin" } });
Defensive patterns

Strategy: validation

Validate before calling

static bool UpdateKeyTypesAreValid(string[] keyColumns, string[]? keyColumnTypes)
{
    return keyColumnTypes == null || keyColumnTypes.Length == keyColumns.Length;
}

// usage
if (!UpdateKeyTypesAreValid(keyColumns, keyColumnTypes))
    throw new InvalidOperationException("keyColumnTypes.Length must equal keyColumns.Length (or be null)");

Try / catch

try
{
    await dbContext.Database.MigrateAsync();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("number of key column types") && ex.Message.Contains("data modification"))
{
    logger.LogError(ex, "An UpdateDataOperation has a keyColumnTypes/keyColumns length mismatch; fix the migration.");
    throw;
}

Prevention

When it happens

Trigger: A migration calls migrationBuilder.UpdateData with keyColumnTypes: set to an array whose length differs from keyColumns:. For example keyColumns: new[] { "Id", "TenantId" } with keyColumnTypes: new[] { "int" }. Common after editing a migration and removing a key column without removing its type entry.

Common situations: Manually adding keyColumnTypes and miscounting. Editing a model-less migration and forgetting a key type entry. Refactoring a composite key without updating the type array.

Related errors


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