{"record":{"id":"2470b014015e30a9","repo":"dotnet/efcore","slug":"the-number-of-key-column-types-typescount-does","errorCode":null,"errorMessage":"The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data deletion operation on '{table}'. Provide the same number of key column types and key columns.","messagePattern":"The number of key column types \\((.+?)\\) doesn't match the number of key columns \\((.+?)\\) for the data deletion operation on '(.+?)'\\. Provide the same number of key column types and key columns\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs","lineNumber":977,"sourceCode":"    /// </summary>\n    /// <param name=\"operation\">The data operation to generate commands for.</param>\n    /// <param name=\"model\">The model.</param>\n    /// <returns>The commands that correspond to the given operation.</returns>\n    protected virtual IEnumerable<IReadOnlyModificationCommand> GenerateModificationCommands(\n        DeleteDataOperation operation,\n        IModel? model)\n    {\n        if (operation.KeyColumns.Length != operation.KeyValues.GetLength(1))\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.DeleteDataOperationValuesCountMismatch(\n                    operation.KeyValues.GetLength(1), operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));\n        }\n\n        if (operation.KeyColumnTypes != null\n            && operation.KeyColumns.Length != operation.KeyColumnTypes.Length)\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.DeleteDataOperationTypesCountMismatch(\n                    operation.KeyColumnTypes.Length, operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));\n        }\n\n        if (operation.KeyColumnTypes == null\n            && model == null)\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.DeleteDataOperationNoModel(\n                    FormatTable(operation.Table, operation.Schema)));\n        }\n\n        var keyPropertyMappings = operation.KeyColumnTypes == null\n            ? GetPropertyMappings(operation.KeyColumns, operation.Table, operation.Schema, model)\n            : null;\n\n        for (var i = 0; i < operation.KeyValues.GetLength(0); i++)\n        {","sourceCodeStart":959,"sourceCodeEnd":995,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs#L959-L995","documentation":"Thrown inside GenerateModificationCommands(DeleteDataOperation, ...) in src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs:977 (message key DeleteDataOperationTypesCountMismatch). It throws InvalidOperationException when operation.KeyColumnTypes is non-null but its length differs from operation.KeyColumns.Length. KeyColumnTypes is an optional explicit type override; if supplied, it must align element-for-element with KeyColumns.","triggerScenarios":"A migration calls migrationBuilder.DeleteData with keyColumnTypes: set to an array whose length differs from keyColumns:. For example keyColumns: new[] { \"Id\", \"TenantId\" } with keyColumnTypes: new[] { \"int\" }. Common after hand-editing a migration and removing a key column but not its type.","commonSituations":"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.","solutions":["Make operation.KeyColumnTypes either null (types resolved from the model) or an array of exactly operation.KeyColumns.Length.","Re-scaffold the migration to regenerate consistent keyColumnTypes.","Map the table to an entity type so keyColumnTypes can be omitted entirely.","Audit: for every i, KeyColumns[i] must pair with KeyColumnTypes[i]."],"exampleFix":"// before (2 key columns, 1 key type):\nmigrationBuilder.DeleteData(\n    table: \"Members\",\n    keyColumns: new[] { \"UserId\", \"GroupId\" },\n    keyColumnTypes: new[] { \"int\" },\n    keyValues: new object[,] { { 7, 3 } });\n\n// after (aligned):\nmigrationBuilder.DeleteData(\n    table: \"Members\",\n    keyColumns: new[] { \"UserId\", \"GroupId\" },\n    keyColumnTypes: new[] { \"int\", \"int\" },\n    keyValues: new object[,] { { 7, 3 } });","handlingStrategy":"validation","validationCode":"static bool DeleteKeyTypesAreValid(string[] keyColumns, string[]? keyColumnTypes)\n{\n    return keyColumnTypes == null || keyColumnTypes.Length == keyColumns.Length;\n}\n\n// usage\nif (!DeleteKeyTypesAreValid(keyColumns, keyColumnTypes))\n    throw new InvalidOperationException(\"keyColumnTypes.Length must equal keyColumns.Length (or be null)\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await dbContext.Database.MigrateAsync();\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"number of key column types\") && ex.Message.Contains(\"data deletion\"))\n{\n    logger.LogError(ex, \"A DeleteDataOperation has a keyColumnTypes/keyColumns length mismatch; fix the migration.\");\n    throw;\n}","preventionTips":["Omit keyColumnTypes when an entity maps the table.","When supplying keyColumnTypes, keep them in the same order and count as keyColumns.","Re-scaffold migrations after key changes rather than hand-editing.","Add a test asserting keyColumnTypes == null || keyColumnTypes.Length == keyColumns.Length."],"tags":["migrations","data-seeding","delete-data","validation","invalidoperation"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}