Skip to content

Rowversion migration regression from EF Core 8 to 9 #35148

Closed as duplicate

Description

@Moerup

Rowversion migration regression from EF Core 8 to 9

I'm in the process of upgrading an application from .NET 8 to 9.

But I'm running into problems when trying to run existing migrations that have been created with EF Core 8 and working for over a year now on multiple versions of EF Core (8.0.4 to 8.0.11)

I have a migration that throws exceptions and can't complete at all on v9.0.0, but if I revert to 8.0.11 it works fine.

Include your code

The migration that fails is a rename of a table where we use rowversion configured like this:

public void Configure(EntityTypeBuilder<Sensor> builder)
{
    builder.ToTable(nameof(MasterCatalogueDbContext.Sensors), schema: DatabaseConstants.MasterDataCatalogueSchemaName);

    // Optimistic Concurrency token
    builder.Property(p => p.RowVersion).IsRowVersion();
}

With this migration created for altering the rowversion column:

migrationBuilder.AlterColumn<byte[]>(
    name: "RowVersion",
    schema: "attributeList",
    table: "MeasurementUnits",
    type: "rowversion",
    rowVersion: true,
    nullable: false,
    defaultValue: Array.Empty<byte>(),
    oldClrType: typeof(byte[]),
    oldType: "rowversion",
    oldRowVersion: true,
    oldDefaultValue: Array.Empty<byte>())
    .Annotation("SqlServer:IsTemporal", true)
    .Annotation("SqlServer:TemporalHistoryTableName", "MeasurementUnitsHistory")
    .Annotation("SqlServer:TemporalHistoryTableSchema", "attributeList")
    .Annotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd")
    .Annotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart")
    .OldAnnotation("SqlServer:IsTemporal", true)
    .OldAnnotation("SqlServer:TemporalHistoryTableName", "SensorUnitsHistory")
    .OldAnnotation("SqlServer:TemporalHistoryTableSchema", "attributeList")
    .OldAnnotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd")
    .OldAnnotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart");

And that throws this error:

2024-11-14T15:17:05 [15:17:05 INF] Applying migration '20240610063634_RenameSensorUnitToMeasurementUnit'.
2024-11-14T15:17:05 [15:17:05 ERR] Failed executing DbCommand (32ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
2024-11-14T15:17:05 DECLARE @var15 sysname;
2024-11-14T15:17:05 SELECT @var15 = [d].[name]
2024-11-14T15:17:05 FROM [sys].[default_constraints] [d]
2024-11-14T15:17:05 INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
2024-11-14T15:17:05 WHERE ([d].[parent_object_id] = OBJECT_ID(N'[attributeList].[MeasurementUnits]') AND [c].[name] = N'RowVersion');
2024-11-14T15:17:05 IF @var15 IS NOT NULL EXEC(N'ALTER TABLE [attributeList].[MeasurementUnits] DROP CONSTRAINT [' + @var15 + '];');
2024-11-14T15:17:05 ALTER TABLE [attributeList].[MeasurementUnits] ALTER COLUMN [RowVersion] rowversion NOT NULL;
2024-11-14T15:17:05 ALTER TABLE [attributeList].[MeasurementUnits] ADD DEFAULT 0x FOR [RowVersion];
2024-11-14T15:17:05 [15:17:05 ERR] Failed to migrate MasterCatalogue database!
2024-11-14T15:17:05 Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot alter column 'RowVersion' to be data type timestamp.
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean isAsync, Int32 timeout, Boolean asyncWrite)
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName)
2024-11-14T15:17:05    at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery()
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.Execute(IReadOnlyList`1 migrationCommands, IRelationalConnection connection, MigrationExecutionState executionState, Boolean beginTransaction, Boolean commitTransaction, Nullable`1 isolationLevel)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.<>c.<ExecuteNonQuery>b__3_1(DbContext _, ValueTuple`6 s)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IReadOnlyList`1 migrationCommands, IRelationalConnection connection, MigrationExecutionState executionState, Boolean commitTransaction, Nullable`1 isolationLevel)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.MigrateImplementation(DbContext context, String targetMigration, MigrationExecutionState state, Boolean useTransaction)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c.<Migrate>b__20_1(DbContext c, ValueTuple`4 s)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
2024-11-14T15:17:05    at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
2024-11-14T15:17:05    at RTDT.MasterCatalogue.Web.Program.MigrateAndSeed(WebApplication app) in C:\GitVestas\RTDT\src\Services\RTDT.MasterCatalogue\src\RTDT.MasterCatalogue.Web\Program.cs:line 171
2024-11-14T15:17:05 ClientConnectionId:f4a440da-ec8a-43b2-9051-87cbd87b6faa
2024-11-14T15:17:05 Error Number:4927,State:1,Class:16

Include provider and version information

EF Core version: 9.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 9.0
Operating system: Windows & Linux (Local & CICD)
IDE: Visual Studio Enterprise 2022 17.12.0

Activity

  1. Moerup commented on Nov 19, 2024

    @Moerup
    Author

    Extracted the rowversion issue from this other temporal issue: #35108 based on @roji comment: #35108_

  2. ajcvickers commented on Dec 4, 2024

    @ajcvickers
    Contributor

    @Moerup I have not been able to reproduce this. Please attach a small, runnable project or post a small, runnable code listing that contains a full model in code that produces the initial migration and then specifically how to change that model to reproduce the migration that you are seeing.

  3. Moerup commented on Dec 4, 2024

    @Moerup
    Author

    @ajcvickers Have you tried testing it with a table using temporal tables?
    I have another issue open, I also mentioned in the other comment (#35108), and maybe they are clashing and it's not related directly to the rowversion.

    I sadly don't currently have the resources in the project to spend time on a standalone reproduction of the issues, would love to do it, but the money guys says no, as is tradition 😄

  4. danroot commented on Dec 4, 2024

    @danroot

    @ajcvickers I'm not 100% sure, but my repro from the other ticket may help here also. The issue there is a migration created by EFCore 8 that is then applied by EFCore 9. In that case, we have unit tests that recreate the database using migrations. These tests fail when switching to EFCore 9, running EFCore 8-generated migration for a temporal table. I suspect similar in this case, and can try to fork the repository to prove it out. In general:

    • Create a project with EF Core 8 db context
    • Add an initial migration
    • Update the tables to use RowVersion in OnModelCreating of the db context: builder.Property(p => p.RowVersion).IsRowVersion();
    • Add a migration for the change
    • Update the Microsoft.EntityFrameworkCore.* nuget packages to to EF Core 9
    • Run the migration by calling context.Database.Migrate or using dotnet ef database update
  5. danroot commented on Dec 4, 2024

    @danroot

    Full repro of this error. It's worth noting I could not get it to repro if I just had RowVersion migration. The issue is a combination of RowVersion+TemporalTable migrations generated from EFCore8, when run by EFCore9 causes the error "Cannot alter column 'RowVersion' to be data type timestamp". This is a different error from the other ticket, but I suspect being caused by the same area of EFCore9 code. I suspect these changes, but haven't dove into the EFCore code to try to figure out further.

    In our case, the related issue is blocking EFCore9 adoption. Possible workarounds include modifying the migration cs somehow or complete reset of migrations.

  6. Moerup commented on Dec 4, 2024

    @Moerup
    Author

    Thanks a lot @danroot for taking the time to make repros of not only 1, but 2 of my created issues!

  7. added a commit that references this issue on Dec 6, 2024
    d231ccb
  8. added 2 commits that reference this issue on Dec 6, 2024
    9f5e228
    f4a591c
  9. killnine commented on Jan 6, 2025

    @killnine

    Thanks for the legwok @danroot, this regression hit my project too as I was updating to .NET 9. I have the exact situation where my solution originally did not have temporal tables applied to entities but they were added later. Now my CI pipeline fails with the error about

    Cannot alter column 'Version' to be data type timestamp

    Pretty tricky and will prevent this update from going to production. So does a fix look to be heading for 9.0.2?

  10. danroot commented on Jan 7, 2025

    @danroot

    Yes, my understanding is this is fixed for 9.0.2 in #35289

    If you need fix before that, @maumar 's fix here works.. I even looked into writing a script to fixup ours in the way he suggests (we have dozens of migrations with this issue), but couldn't quite get a script I was comfortable with.

    An alternative, IF you don't have to support any old-version databases, would be to squash migrations. and start over. That makes most sense in like an enterprise environment where you know all dev, test, and production instances of the db are up-to-date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions