diff --git a/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs b/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs index d1d6e7e23ff..69a41d1c099 100644 --- a/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs +++ b/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Linq; using System.Text; using Microsoft.EntityFrameworkCore.Sqlite.Internal; using Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal; @@ -373,6 +374,22 @@ private IReadOnlyList RewriteOperations( continue; } + // Skip autoincrement primary key columns that are being added in this migration + var isAutoincrement = column.FindAnnotation(SqliteAnnotationNames.Autoincrement)?.Value as bool? == true; + var isPrimaryKey = column.Table.PrimaryKey?.Columns.Contains(column) == true; + + if (isAutoincrement && isPrimaryKey) + { + // Check if this column is being added in the current migration + var isNewColumn = migrationOperations.OfType() + .Any(op => op.Table == key.Table && op.Schema == key.Schema && op.Name == column.Name); + + if (isNewColumn) + { + continue; // Skip newly added autoincrement columns + } + } + if (first) { first = false; diff --git a/test/EFCore.Sqlite.FunctionalTests/Migrations/MigrationsSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Migrations/MigrationsSqliteTest.cs index c97206ec9af..43cc3985995 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Migrations/MigrationsSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Migrations/MigrationsSqliteTest.cs @@ -2275,6 +2275,79 @@ protected override bool AssertConstraintNames protected override string NonDefaultCollation => "NOCASE"; + [ConditionalFact] + public virtual async Task Replace_string_primary_key_with_autoincrement_identity() + { + await Test( + builder => builder.Entity( + "Person", e => + { + e.Property("Ssn"); + e.HasKey("Ssn"); + }), + builder => { }, + builder => builder.Entity( + "Person", e => + { + e.Property("Id").ValueGeneratedOnAdd(); + e.Property("Ssn"); + e.HasKey("Id"); + e.HasIndex("Ssn").IsUnique(); + }), + model => + { + var table = Assert.Single(model.Tables); + Assert.Equal("Person", table.Name); + Assert.Equal(2, table.Columns.Count()); + + var idColumn = Assert.Single(table.Columns, c => c.Name == "Id"); + Assert.False(idColumn.IsNullable); + }); + + // Expectation: the INSERT should NOT include the Id column because it's AUTOINCREMENT + AssertSql( + """ +ALTER TABLE "Person" ADD "Id" INTEGER NOT NULL DEFAULT 0; +""", + // + """ +CREATE UNIQUE INDEX "IX_Person_Ssn" ON "Person" ("Ssn"); +""", + // + """ +CREATE TABLE "ef_temp_Person" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Person" PRIMARY KEY AUTOINCREMENT, + "Ssn" TEXT NULL +); +""", + // + """ +INSERT INTO "ef_temp_Person" ("Ssn") +SELECT "Ssn" +FROM "Person"; +""", + // + """ +PRAGMA foreign_keys = 0; +""", + // + """ +DROP TABLE "Person"; +""", + // + """ +ALTER TABLE "ef_temp_Person" RENAME TO "Person"; +""", + // + """ +PRAGMA foreign_keys = 1; +""", + // + """ +CREATE UNIQUE INDEX "IX_Person_Ssn" ON "Person" ("Ssn"); +"""); + } + protected virtual async Task AssertNotSupportedAsync(Func action, string? message = null) { var ex = await Assert.ThrowsAsync(action);